この記事は「PowerCMS X Advent Calendar 2025」の8日目の記事です。

今年はClaude CodeCursorを利用してプラグイン開発を行う時間が激増しました。AIの進化には驚かされるばかりです。AIを利用しての所感は9月に書いた記事「Claude CodeとCursorで実現するPowerCMS Xプラグイン開発の効率化」にまとめていますので、こちらもご覧いただければと思います。

プロパティのチェックでハマる

さて、AIを利用した開発の中で少しハマったことがありますのでご紹介します。AIが出力したコードによくif ( empty( $obj->column_name ) )のような記述が出てくるのですが、どうも意図した結果になっていないようなのです。

例えば$user->totp_enabled1(つまりユーザーモデルのとあるオブジェクトのtotp_enabledカラムにチェックが入っている状態)だとして、下記の出力結果はどうなるでしょうか?

var_dump( $user->totp_enabled );
echo '<br>';

if ( $user->totp_enabled ) {
    echo 'Enabled' . '<br>';
} else {
    echo 'Not enabled' . '<br>';
}

if ( empty( $user->totp_enabled ) ) {
    echo 'Empty' . '<br>';
} else {
    echo 'Not empty' . '<br>';
}

期待値は下記の通りだと思います。

string(1) "1"
Enabled
Not Empty

しかし、実際は下記のようにEmptyが表示されます。あれれ?

string(1) "1"
Enabled
Empty

画面キャプチャ:テストコードの処理結果をブラウザに表示した様子

原因と対策

AIに調査させると「PADOでは__isset()マジックメソッドが実装されていないため、isset()empty()が正しく動作しません。」との回答でした。モデル編集画面にて自由に命名されたカラム名でデータベースの登録内容を取得できるようにはマジックメソッドを利用する必要があるのですが、__get()マジックメソッドのみ実装されているということですね。$user->totp_enabledを一度変数に入れるか、否定形で直接比較するようにしましょう。(わざわざempty()を使わなくても…という話なのかもしれないのですが、そのことを論じると話が逸れるのでいったん置いておきます。)

$totp_enabled = $user->totp_enabled;

if ( empty( $totp_enabled ) ) {
    echo 'Empty' . '
'; } else { echo 'Not empty' . '
'; }
一度変数に入れる方式
if ( ! $user->totp_enabled ) {
    echo 'Empty' . '
'; } else { echo 'Not empty' . '
'; }
否定型を利用する方式

Claude Codeに「この事実をCLAUDE.mdにメモしておいて」とお願いすると以下のようになりました。これからはこの指示に従って実装をしてくれるはずです。

#### プロパティの評価
PADOのオブジェクトを直接`empty`等で評価しないでください。PADOでは`__isset()`マジックメソッドが実装されていないため、`isset()`や`empty()`が正しく動作しません。

```php
// ❌ 正しく動作しない
if (empty($user->totp_enabled)) { }

// ✅ 一度変数に代入してから評価
$totp_enabled = $user->totp_enabled;
if (empty($totp_enabled)) { }

// ✅ または否定形で直接比較
if (!$user->totp_enabled) { }
```

PADOに__issetマジックメソッドを実装してみると…

オブジェクトやマジックメソッドは奥が深いですね。私ももっと知識を深めていかなければならないと感じました。

ひとまずClaude Codeに__isset()マジックメソッドの実装をお願いしてみると以下のようになりました。ただ、__get()マジックメソッドにも手を入れる必要があり(未定義プロパティへのアクセス警告を回避するため)、少し大きなアップデートになってしまいそうです。

/**
 * Check if a property is set.
 * Handles column prefix and relational properties.
 *
 * @param  string $col : Column name without prefix.
 * @return bool        : True if property exists.
 */
public function __isset ( string $col ): bool {
    $original = $col;
    $colprefix = $this->_colprefix;

    // Get all properties including dynamic ones
    $vars = get_object_vars( $this );

    // Check without prefix first
    if ( array_key_exists( $col, $vars ) ) {
        return true;
    }

    // Check with prefix
    if ( $colprefix ) {
        $prefixed_col = $colprefix . $col;
        if ( array_key_exists( $prefixed_col, $vars ) ) {
            return true;
        }
    }

    // Check for relational property (e.g., {property}_id)
    $rel_col = $original . '_id';
    if ( array_key_exists( $rel_col, $vars ) ) {
        return true;
    }
    if ( $colprefix ) {
        $prefixed_rel_col = $colprefix . $rel_col;
        if ( array_key_exists( $prefixed_rel_col, $vars ) ) {
            return true;
        }
    }

    return false;
}
Claude Codeで生成した_isset()マジックメソッド

__isset()マジックメソッドを実装したPADOを使うと、先のコードの実行結果は期待値が得られるようになりました。

string(1) "1"
Enabled
Not Empty