0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

PHP「Cannot use object of type stdClass as array」錯誤:物件與陣列存取的差異與解法

cannot use object of type stdclass as array joomla

網站突然出現這樣的錯誤,頁面整個停住:

純文字
Fatal error: Uncaught Error: Cannot use object of type stdClass as array in /home/.../plugins/system/helix3/helix3.php:54

意思是:程式用陣列的方式($data['title'])去讀取一個「物件」,PHP 不允許,執行就中斷了。這個錯誤不是 Joomla 專屬,WordPress 外掛、自己寫的 PHP 程式都會遇到,原因和解法都一樣。

物件和陣列,差在哪裡?

PHP 裡有兩種常見的「裝資料的容器」:

  • 陣列(array):用中括號讀取,例如 $data['title']。
  • 物件(object):用箭頭讀取,例如 $data->title。stdClass 是 PHP 最基本、沒有任何功能的物件,常出現在 json_decode() 和資料庫查詢的結果裡。

兩者裝的資料可以一模一樣,但讀取的寫法不能混用:

PHP
$arr = ['title' => '網頁設計'];
echo $arr['title'];   // 正確
echo $arr->title;     // 錯誤:Attempt to read property "title" on array

$obj = new stdClass();
$obj->title = '網頁設計';
echo $obj->title;     // 正確
echo $obj['title'];   // 錯誤:Cannot use object of type stdClass as array

反過來把陣列當物件讀,PHP 8 的訊息是 Attempt to read property "title" on array(PHP 7 是 Trying to get property ... of non-object),原因相同,只是方向相反。

最常見的原因:JSON 解碼時的第二個參數

從 API、資料庫欄位讀到 JSON 字串,用 json_decode() 轉換時,第二個參數決定你拿到的是物件還是陣列:

PHP
$json = '{"name":"小明","address":{"city":"基隆"},"tags":["web","seo"]}';

// 不加第二個參數:得到物件
$obj = json_decode($json);
echo $obj->name;            // 小明
echo $obj->address->city;   // 基隆
echo $obj->tags[0];         // web(JSON 的陣列仍然是 PHP 陣列)

// 第二個參數設成 true:得到陣列
$arr = json_decode($json, true);
echo $arr['name'];             // 小明
echo $arr['address']['city'];  // 基隆
echo $arr['tags'][0];          // web

所以看到這個錯誤,先找出那份資料是從哪裡來的:

  • 如果是 json_decode($json) 出來的,改成 json_decode($json, true),後面的程式就能繼續用 []。
  • 或是反過來,不改 json_decode(),把後面的 $data['name'] 改成 $data->name。

兩種擇一,同一份資料從頭到尾用同一種寫法,比較不會出錯。

另外,JSON 格式錯誤時 json_decode() 會回傳 null,後面的程式一樣會出錯。PHP 7.3 以上可以加上 JSON_THROW_ON_ERROR,格式錯誤時直接丟出例外,比較容易除錯:

PHP
try {
    $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    $data = [];
}

Joomla 裡常見的情境

資料庫查詢:loadObjectList 與 loadAssocList

Joomla 的資料庫物件有好幾種讀取結果的方法,回傳的格式不一樣:

PHP
use Joomla\CMS\Factory;

$db    = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true)
    ->select($db->quoteName(['id', 'title']))
    ->from($db->quoteName('#__content'))
    ->where($db->quoteName('state') . ' = 1');
$db->setQuery($query);

$rows = $db->loadObjectList();  // 每一筆是物件:$rows[0]->title
$rows = $db->loadAssocList();   // 每一筆是陣列:$rows[0]['title']

只取一筆的 loadObject() 與 loadAssoc() 也是同樣的道理。

文章的圖片、連結欄位

Joomla 文章的圖片(images)、連結(urls)、顯示選項(attribs)在資料庫裡是 JSON 字串,要先解碼才能用:

PHP
$images = json_decode($item->images);
$intro  = $images->image_intro;      // 物件寫法

$images = json_decode($item->images, true);
$intro  = $images['image_intro'];    // 陣列寫法

原文的狀況:Helix3 外掛

這篇文章舊版記錄的是 Joomla 3 的 Helix3 樣板框架:plugins/system/helix3/helix3.php 在某些情況下收到的 $data 是物件,程式卻用陣列的方式讀取。當時的解法是在使用前先統一轉成陣列:

PHP
$data = is_object($data) ? (array) $data : $data;

這個思路到現在都適用(下一節會再說明),但不建議直接修改外掛或樣板的檔案,擴充套件一更新,改的地方就會被覆蓋。比較好的做法是:

  • 先更新到最新版,這類錯誤通常已經被開發商修正。
  • Helix3 是 Joomla 3 用的框架,Joomla 3 已經停止支援;JoomShaper 在 Joomla 4/5 對應的框架是 Helix Ultimate,建議連同網站一起升級。
  • 真的需要暫時修改,記下改了哪個檔案、哪一行,每次更新後都要再檢查。

不確定拿到的是物件還是陣列時

Joomla 有些事件、WordPress 有些 filter,傳進來的資料依情況可能是物件、也可能是陣列。這時先統一格式再處理:

PHP
if (is_object($data)) {
    $data = get_object_vars($data);
}
$title = $data['title'] ?? '';
  • get_object_vars() 和 (array) 轉型都只轉換第一層:$data['address'] 如果原本是物件,轉完還是物件。
  • 需要整份資料(包含裡面每一層)都變成陣列,可以用 json_decode(json_encode($data), true),但只適合單純的資料(例如 API 回傳的內容),效能也比較差。
  • 只是想安全地讀一個值,可以用 ??,例如 $city = $obj->address->city ?? '';,中間任何一層不存在都會回傳空字串,不會出現錯誤。

WordPress 裡的例子

  • get_post()、get_posts() 回傳的是 WP_Post 物件,要用 $post->post_title,不能用 $post['post_title'](錯誤訊息會是 Cannot use object of type WP_Post as array)。
  • $wpdb->get_results() 預設回傳物件;第二個參數傳 ARRAY_A 就會變成陣列。
  • 用 wp_remote_get() 呼叫外部 API 取得的 JSON,同樣要注意 json_decode() 的第二個參數。
PHP
global $wpdb;
$sql = "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_status = 'publish' LIMIT 5";

foreach ( $wpdb->get_results( $sql ) as $row ) {
    echo esc_html( $row->post_title );    // 預設:物件
}

foreach ( $wpdb->get_results( $sql, ARRAY_A ) as $row ) {
    echo esc_html( $row['post_title'] );  // ARRAY_A:陣列
}

除錯小技巧

不知道變數到底是物件還是陣列,印出來看最快:

PHP
var_dump($data);

開頭寫 object(stdClass) 就是物件,array(...) 就是陣列。在正式網站上除錯,改用 error_log(print_r($data, true)); 寫進錯誤記錄,不要讓訪客看到,看完記得刪掉。

另外,PHP 8 對型別的檢查更嚴格,例如對物件使用 count() 會直接出錯。舊程式在 PHP 7 只是跳出警告、還能勉強執行的寫法,升級到 PHP 8 後可能變成整頁當掉,升級 PHP 前最好先在測試環境跑過一次。

延伸閱讀

外掛、樣板在 PHP 或系統升級後冒出錯誤,是舊網站很常見的狀況。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與網站維護託管,歡迎來聊聊。

分享這篇文章

返回文章列表更多「joomla技術教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!