ネットでの販売を外のサービスに任せることになり、ネットショップのプラグイン Welcart(usc-e-shop)を止めることにしました。
ところが、作業環境で Welcart を止めたら、どのページも 500(Fatal error)になりました。
この記事では、原因と直し方、止める前に確認しておくことをまとめます。
何が起きたか
Welcart を止めて、トップのページを開くと、500 が返りました。
トップだけでなく、どのページも同じでした。
原因は、テーマの header.php でした。
ヘッダーは全部のページで読まれるので、ここで止まると、全部のページが 500 になります。
原因:条件を逆にして、プラグインの定数を使っていた
header.php には、スマホ用のカートのボタンがありました。
このボタンを出さないようにするため、前に誰かが条件を逆にしていました。
<?php // 会員・カートのナビ(Welcart が動いているときだけ出す) if ( is_welcart_active() ) : // …… endif; // スマホのカートのボタン if ( ! is_welcart_active() ) : // ← 条件が逆 ?> <a href="<?php echo esc_attr( USCES_CART_URL ); ?>" class="c-cart-button"> <span class="badge"><?php echo usces_totalquantity_in_cart(); ?></span> </a> <?php endif; ?>
| 書き方 | 役目 |
|---|---|
is_welcart_active() |
テーマの関数。Welcart が動いていれば true |
! is_welcart_active() |
Welcart が動いていないときに true |
USCES_CART_URL |
Welcart が決める定数(カートのページの URL) |
usces_totalquantity_in_cart() |
Welcart の関数(カートの中の数) |
is_welcart_active() の内容は、次のとおりです。
function is_welcart_active() {
global $usces;
return defined( 'USCES_VERSION' ) && $usces;
}
Welcart が動いているあいだは、! is_welcart_active() が false なので、ボタンは出ません。
見た目は「ボタンを隠した」ように見えるので、ずっと気づかれていませんでした。
Welcart を止めると、条件が true になり、ボタンを出そうとします。
そのとき、Welcart の定数 USCES_CART_URL がないので、PHP が止まりました。
PHP 8 では、ない定数を使うと Error になります(PHP 7 では警告を出して、定数の名前を文字として使っていました)。
try / catch では受けにくい
「エラーになるなら try / catch で囲めばよいのでは」と思うかもしれません。
そこで、作業環境の PHP 8.3 で、検証用スクリプトを流して確認しました。
<?php
// 検証用スクリプト:プラグインの定数・関数・クラスがないとき、try / catch でどうなるか
try {
try {
echo MY_PLUGIN_CART_URL; // ない定数
} catch ( Exception $e ) {
echo 'Exception で受けた';
}
} catch ( Error $e ) {
echo get_class( $e ) . ' / ' . $e->getMessage();
}
| 試したこと | catch ( Exception $e ) / catch ( Error $e ) |
|---|---|
| ない定数を使う | catch ( Exception $e ):受けられないcatch ( Error $e ):受けた(Undefined constant) |
| ない関数を呼ぶ | catch ( Exception $e ):受けられないcatch ( Error $e ):受けた(Call to undefined function) |
| ないクラスを new する | catch ( Exception $e ):受けられないcatch ( Error $e ):受けた(Class not found) |
ないものを使ったときに出るのは Exception ではなく Error なので、ふつうの catch ( Exception $e ) では受けられません。
catch ( Error $e ) なら受けられますが、テンプレートのあちこちを try で囲むのは現実的ではありません。
また、受けたあとに何を出すかも決められません。
そこで、使う前に「プラグインがあるか」を確認して、ないときは使わない(または代わりのものを出す)形にしました。
直し方 1:使わない部分は外す
ネットショップは外のサービスに任せるので、カートのボタンはもう必要ありません。
条件を直すより、ボタンの部分ごと外すのがいちばん簡単で確かです。
<?php // スマホのカートのボタンは出さない(ネットショップは外のサービスに任せる)。 // 前は条件を逆にして隠していたため、Welcart を止めると USCES_CART_URL がなく 500 になった ?>
外した理由をコメントで残しておくと、あとで見た人が迷いません。
ヘッダーには、ほかにも Welcart の会員・カートのナビがありましたが、こちらは if ( is_welcart_active() ) の中にあったので、そのままにしました。
直し方 2:プラグインがあるかを確認する分岐
ヘッダーのほかにも、テーマの中には Welcart の関数・定数・テンプレート・ウィジェットを使っているところがありました。
店頭で売っている商品の記事は、Welcart の商品の形で作ってあり、これからも見せたいページです。
そこで、Welcart がなくても同じ見た目で出るように、プラグインがあるかを確認する分岐と、ないときの代わりの表示を足しました。
| 使っているもの | 確認の方法 |
|---|---|
関数(usces_is_item() など) |
function_exists() |
定数(USCES_ITEM_CAT_PARENT_ID など) |
defined() |
| クラス(ウィジェット) | class_exists() |
関数:ないときはテーマの関数で同じことをする
// 商品の記事かを見分ける(プラグインがあればプラグインの関数、なければテーマの関数)
if ( function_exists( 'usces_is_item' )
? usces_is_item( $post_id )
: ( $post_id && my_is_shop_item( $post_id ) ) ) {
// 商品のカテゴリーを探す ……
}
// テーマの関数:プラグインと同じく、記事の post_mime_type で見分ける
function my_is_shop_item( $post_id = null ) {
$post = get_post( $post_id );
return $post && 'post' === $post->post_type && 'item' === $post->post_mime_type;
}
| 書き方 | 役目 |
|---|---|
function_exists( 'usces_is_item' ) |
Welcart の関数があるかを確認する |
? usces_is_item( $post_id ) |
あれば、Welcart の関数で見分ける |
: my_is_shop_item( $post_id ) |
なければ、テーマの関数で見分ける |
post_mime_type が item |
Welcart は商品の記事にこの目印を付けている。プラグインを止めても DB には残る |
Welcart の usces_is_item() も同じ目印で見分けているので、止める前と後で結果が変わりません。
定数:ないときは別の方法で同じ値を探す
// 商品のカテゴリーの ID:プラグインの定数があればそれ、なければスラッグから探す
function my_shop_item_parent_cat_id() {
if ( defined( 'USCES_ITEM_CAT_PARENT_ID' ) ) {
return (int) USCES_ITEM_CAT_PARENT_ID;
}
$term = get_term_by( 'slug', 'item', 'category' );
return $term ? (int) $term->term_id : 0;
}
| 書き方 | 役目 |
|---|---|
defined( 'USCES_ITEM_CAT_PARENT_ID' ) |
Welcart の定数があるかを確認する |
get_term_by( 'slug', 'item', 'category' ) |
なければ、商品のカテゴリーをスラッグから探す |
パンくずの「商品」のところも、定数を直接使わず、この関数を使うように変えました。
商品のページ:テーマのテンプレートで出す
商品のページは、Welcart のテンプレートの仕組みで出していました。
Welcart を止めると、その仕組みごとなくなります。
そこで、商品のページはテーマのテンプレートで出すようにしました。
// 商品のページは、プラグインの有無にかかわらずテーマのテンプレートで出す
function my_shop_item_template( $template ) {
if ( is_single() && my_is_shop_item( get_queried_object_id() ) ) {
$file = locate_template( 'template-parts/shop-item-single.php' );
if ( $file ) {
return $file;
}
}
return $template;
}
add_filter( 'template_include', 'my_shop_item_template', 99 );
| 書き方 | 役目 |
|---|---|
template_include |
WordPress がページに使うテンプレートを決める直前に呼ばれるフィルター |
locate_template() |
テーマ(子テーマがあれば子テーマ)の中のファイルを探す |
99 |
ほかのフィルターより後に動かして、こちらを優先する |
新しいテンプレートは、前の商品のページと同じ形(画像・本文・タブ)で、値段とカートだけを出さないものにしました。
カートのあった場所には「店頭で販売中です」の枠を出しています。
Welcart が動いていても止めていても、同じテンプレートを使うので、止める前に見た目を確認できます。
テンプレートの中でも、Welcart の関数は呼ぶ前に確認します。
// テンプレートの中でも、プラグインの関数は呼ぶ前に確認する
if ( function_exists( 'usces_remove_filter' ) ) {
usces_remove_filter();
}
ウィジェット:同じ ID の代わりのウィジェットを登録する
サイドバーには、Welcart の「商品カテゴリー」のウィジェットを置いていました。
Welcart を止めると、このウィジェットは消えて、サイドバーから見えなくなります。
そこで、Welcart のクラスがないときだけ、同じ ID の代わりのウィジェットを登録しました。
// サイドバーの商品カテゴリー:プラグインのウィジェットと同じ ID の代わりのウィジェット
function my_shop_register_category_widget() {
if ( class_exists( 'Welcart_category' ) ) {
return; // プラグインが動いているときは、プラグインのウィジェットのまま
}
register_widget( 'My_Shop_Category_Widget' );
}
add_action( 'widgets_init', 'my_shop_register_category_widget', 20 );
class My_Shop_Category_Widget extends WP_Widget {
public function __construct() {
// ID と class をプラグインと同じにすると、置いたウィジェットの設定と CSS がそのまま使われる
parent::__construct( 'welcart_category', '商品カテゴリー(代わり)', array( 'classname' => 'widget_welcart_category' ) );
}
public function widget( $args, $instance ) {
$title = isset( $instance['title'] ) ? $instance['title'] : '';
$cat_slug = ! empty( $instance['cat_slug'] ) ? $instance['cat_slug'] : 'itemgenre';
echo $args['before_widget'];
if ( '' !== $title ) {
echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
}
echo '<ul class="ucart_widget_body">';
$parent = get_category_by_slug( $cat_slug );
if ( $parent ) {
wp_list_categories( 'child_of=' . (int) $parent->term_id . '&title_li=' );
}
echo '</ul>';
echo $args['after_widget'];
}
}
| 書き方 | 役目 |
|---|---|
class_exists( 'Welcart_category' ) |
Welcart のウィジェットのクラスがあるかを確認する |
register_widget() |
なければ、代わりのウィジェットを登録する |
'welcart_category'(ID) |
Welcart と同じ ID にすると、置いてあったウィジェットとその設定(タイトルなど)がそのまま使われる |
'widget_welcart_category'(class) |
同じ class にすると、今の CSS がそのまま効く |
このほか、商品の一覧に出す画像(Welcart の商品の画像)も、Welcart を止めたときだけテーマで出すようにしました。
確認した結果
作業環境で Welcart を止めて開いた
直したテーマで、作業環境の Welcart を止め、16 ページを開きました。
どのページもエラーは出ませんでした。
止めているあいだは、ページの返事から Set-Cookie と no-store も消えていました(下の「おまけ」)。
Welcart を止めたのは確認のあいだだけで、確認したあとは元に戻しました。
そのあと、本番で Welcart を止めました。
try / catch
上の「try / catch では受けにくい」の表のとおりです(作業環境の PHP 8.3.32)。
おまけ:止めたらページの表示が速くなった
Welcart は、どのページでもセッションを始めていました。
そのため、全部のページが Set-Cookie と Cache-Control: no-store を返していました。
no-store は「このページを残さないで」という意味なので、サーバー(エックスサーバー)のキャッシュが効いていませんでした。
止めたあと、本番のページの返事を見ると、Set-Cookie と no-store はなくなっていました。
| 最初の文字が返るまで | |
|---|---|
| 止める前(作業のときに見た) | 0.3 秒ほど |
| 止めた後(本番で続けて開いた) | 0.03〜0.09 秒(1 回目は 0.3 秒ほどのこともあった) |
止めた後の数は、記事を書くときに本番のトップを 7 回、もう 1 ページを 4 回開いて測ったものです。
使っていないプラグインが、気づかないところで表示を遅くしていることがあります。
ほかのプラグインを止める前に確認すること
今回のことは、Welcart にかぎりません。
テーマや自作のプラグインが、止めるプラグインの関数・定数・クラスを使っていると、同じことが起きます。
止める前に、テーマの中を探します。
# テーマのフォルダで、止めるプラグインの名前・定数・関数の頭を探す(Welcart の例)
grep -rniE "welcart|usces" --include=*.php .
# 定数を、確認せずに使っているところ(defined と同じ行にないもの)
grep -rnE "USCES_[A-Z_]+" --include=*.php . | grep -v "defined("
# 「動いていないとき」に出す分岐(条件が逆になっていないか)
grep -rnE "! ?is_[a-z_]+_active\(" --include=*.php .
| 探すもの | 例 / 見るところ |
|---|---|
| プラグインの名前 | 例:welcart見るところ:プラグイン専用のテンプレート・関数のファイル |
| 関数の頭の文字 | 例:usces_見るところ:function_exists() で確認しているか |
| 定数の頭の文字 | 例:USCES_見るところ:defined() で確認しているか |
| クラスの名前 | 例:Welcart_category見るところ:class_exists() で確認しているか |
| 「動いていないとき」の分岐 | 例:! is_welcart_active()見るところ:中でプラグインのものを使っていないか(今回の原因) |
| ショートコード・ウィジェット | 例:本文やサイドバーに置いたもの見るところ:止めると、そのまま文字で出たり、消えたりする |
- まず作業環境で止めて、主なページを開き、エラーのログを見る
- 確認が終わったら、作業環境のプラグインは元に戻しておく(本番と同じ状態にしておく)
- 本番で止めたあとも、すぐに主なページを開く。何かあれば、プラグインをもう一度有効にすれば戻る
今回は、ほかのプラグイン(Welcart 以外)では試していません。
まとめ
Welcart を止めたので、これから足す商品の記事には、Welcart が付けていた目印(post_mime_type の item)が付きません。テーマの代わりの関数はこの目印で商品を見分けているので、新しい商品の記事をどう作るか(目印を付けるか、カテゴリーで見分けるか)を決めます。

