網頁

顯示具有 wordpress 標籤的文章。 顯示所有文章
顯示具有 wordpress 標籤的文章。 顯示所有文章

2017年6月13日 星期二

Wordpress ajax實作問題

使用wordpress的ajax一般都會hook兩個action,一個開頭為wp_ajax_,一個開頭為wp_ajax_nopriv_,後頭再加上action的名稱。前者是需要login才能使用,後者是不需要。一般使用上,若是覺得不需要login即可使用的ajax功能,會覺得直接使用wp_ajax_nopriv_即可,但實際上發現,未登入至後台是可以正常使用,一旦使用者登入後台沒登出,此功能立即回傳0無作用。對一般使用者而言,會是重大意外事故。

結論:不管是否需要登入,兩個action都要hook,才不會有意外發生。

2016年3月18日 星期五

WP4.2移機安裝問題:Unknow collation: 'utf8mb4_unicode_ci'

一如往常的想將在電腦弄好的wp網站放上server,放入資料庫資料時卻發現回應如下圖

仔細看了下:Unknow collation: 'utf8mb4_unicode_ci'。覺得很怪,以前都是用utf8_unicode_ci,查了下資料,發現The utf8mb4 Upgrade其中提到

In WordPress 4.2, we’re upgrading tables to utf8mb4, when we can. Your site will only upgrade when the following conditions are met:
  • You’re currently using the utf8 character set.
  • Your MySQL server is version 5.5.3 or higher (including all 10.x versions of MariaDB).
  • Your MySQL client libraries are version 5.5.3 or higher. If you’re using mysqlnd, 5.0.9 or higher.
查了下自己的mysql server版本才5.1.x,看來得自行重新安裝一次了。

2015年7月3日 星期五

稍微強化script_tags防止XSS攻擊

用strip_tags用這麼久才知道,原來它沒法完全去除,雖然還是有些東西可以拿掉,如:onmuouse="alert('text')這類,但script tag拿不掉,很容易就中XSS攻擊,參考了下wordpress中的wp_strip_all_tags的寫法,基本上只是增加了pre_replace這行,至少將style或script的內容拿掉就能阻止大多數的XSS攻擊了。

function strip_all($string){
    $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
    $string = strip_tags($string);
    return $string;
}


參考網址:
Function Reference/wp strip all tags

2015年6月28日 星期日

無法重新定義pluggable function: wp_notify_postauthor()

最近接了個維護案,做了更新後,重新啟動plugin,卻出現底下的訊息

Fatal error: Cannot redeclare wp_notify_postauthor() (previously declared in /ae/wp-includes/pluggable.php:1003) in /ae/wp-content/plugins/ae-menu-pages/ae-menu-pages.php on line 120

本以為是更新後造成問題,但查半天發現主要是重新定義wp_notify_postauthor造成的,這個function是個pluggable function,讓開發者可以在plugin中重新定義的,看半天並不覺得有任何問題,直到查到這篇How to Override a Pluggable Function

原文解說如下:
the reason it needs to be wrapped in function_exists is because within the context of a plugin activation, the function does already exist. This is due to the way WordPress activates plugins in a sandbox, which is necessary to enable it to recover from fatal errors in the activated plugin. Technically, once the plugin is activated the function_exists check would not be necessary, but you'd never be able to activate that plugin via conventional means without it. –  Milo Sep 24 '12 at 15:57

試了後確定是這個問題,總之,不管是否這個function是在wordpress載入預設的同名function前就宣告了,就是一定要加入function_exists這個判斷如下:
if( !function_exists() ):
    function wp_notify_postauthor() {
        //執行內容
    }
endif;


參考網址:
How to Override a Pluggable Function
Customizing the new user email with a pluggable function

2014年12月12日 星期五

Wordpress開發筆記-PHP Warning: Cannot modify header information for wp-session.php on line 124

wp session這個plugin真的是蠻好用的,不過使用時一直有類似log出現

[Fri Nov 21 11:55:06 2014] [error] [client ::1] PHP Warning:  Cannot modify header information - headers already sent by (output started at /work/demo/wp-admin/menu-header.php:118) in /work/demo/inc/lib/wp-session-manager/class-wp-session.php on line 124, referer: https://localhost/~alvin/demo/wp-admin/edit.php?post_type=exercise

這個warning猜測是因為正常處理下,最後會執行到wp_session_commit(),但若是用ajax的方式時,或直接用die()結束時,並沒有執行wp_session_commit(),以至於有這個warning,最後實驗結果,果然如我所料,哈哈


參考網址:

2014年8月25日 星期一

Wordpress開發筆記-使用 $wpdb->update 如何知道是沒有更新任何東西還是更新Fail

使用wordpress的wpdb function時,總有個小疑慮,我怎知道目前的update是成功還失敗,因為若因為值相同,所以沒有update,會傳回false,失敗一樣傳回false,查半天才知道,前者是傳回0,後者是真的傳回false,但一般比較都認為相同,所以要用底下這樣寫法比對,才會比對型態的不同,使用$wpdb->update時可以像以下的方式,這邊用pseudo code表示

$suc = $wpdb->update(table_name,
    array( 
 'column1' => 'value1', // string
 'column2' => 'value2'  // integer (number) 
    ), 
    array( 'ID' => 1 ), 
        array( 
     '%s',  // value1
     '%d'   // value2
    ), 
    array( '%d' )
);
if( $suc === false ) {
    //Do something about fail
}
else if( $suc === 0 ){
    //Do something about nothing update
}


參考網址:
$wpdb - what does it return on fail?

2014年7月11日 星期五

Wordpress開發筆記-check If page is something then do something

正好有個需求是在某個叫test的page中時,若是在這個page時沒有login,那麼要導向首頁

function check_not_login() {

    if( is_page('test') && is_user_login() ) {
        wp_redirect(home_url());
        die();
    }
}
add_action('init','check_not_login');

查到這篇Wordpress is_page() always returned with false,再仔細看看Plugin API/Action Referenc的說明,看起來不能用hook在init中,要改用wp

function check_not_login() {

    if( is_page('test') && is_user_login() ) {
        wp_redirect(home_url());
        die();
    }
}
add_action('wp','check_not_login');

像這樣就ok了,對wordpress的action還真的不是太懂

參考網址:
Wordpress is_page() always returned with false
Plugin API/Action Referenc

2014年6月28日 星期六

WordPress開發筆記-更動Theme後發生post的feature image無法顯示預設上傳功能

今天遇到個問題,就是在後台要設定某篇post的feature image時,卻發現沒有跳出預設的上傳視窗,如下圖:



正常是要看到下圖:


查半天查不出來,不知道是不是只有我有這問題,後來比對其他預設的theme,才發現在要加上底下這行:
add_theme_support( 'post-thumbnails' );

我是加在function.php,且定義個my_setup這function,放在它之中,再學其他的theme,寫成
add_action( 'after_setup_theme', 'my_setup' );

這樣就可以正常取得第二張圖的功能囉~

PS. 不知道這是不是3.X之後才有的,之前從來不用加這個的啊~ 還是因為我都用plugin或post_register就用掉了~(抓頭)

2014年6月19日 星期四

WordPress開發筆記-將textarea改用WordPress內建的編輯器

現在要在wordpress中加入內建的編輯器真簡單,只要使用wp_editor就行了

Function Reference/wp editor中,如下
wp_editor( $content, $editor_id, $settings = array() );

例如,要創建一個id及name是description的編輯框,就如下:
$args = array("textarea_rows" => 5, "textarea_name" => "description");
wp_editor($notes, "description", $args);

這樣就可以用了,不過這function只有3.3之後才有囉

參考網址
Add rich text editor(s)… the right way
Function Reference/wp editor

2014年5月14日 星期三

WordPress開發筆記-如何得知目前在Admin的哪個頁面

有時在自訂post type的管理時,需要知道目前正在哪個管理頁面,針對那個頁面做特別處理,之前都是去看post type的值,後來發現其實用get_current_screen比較準確,他的id值就可以知道目前在管理列表或在編輯post,如:

post type為abc
在管理列表頁時,get_current_screenid=edit-abc
在編輯post時,get_current_screenid=abc

若是編輯使用者時,則為
在管理列表頁時,get_current_screenid=users
在編輯post時,get_current_screenid=user-edit

另外,也可以用$pagenow,不過在post type上就不像screen那麼明確,同上例的結果如下
post type為abc
在管理列表頁時,$pagenow=edit.php
在編輯post時,$pagenow=post.php

若是編輯使用者時,則為
在管理列表頁時,$pagenow=users.php
在編輯post時,$pagenow=user-edit.php

2014年3月16日 星期日

WordPress開發筆記-取得theme的絕對路徑

這邊記錄一下,要取得WordPress中目前正在使用的theme的實際目錄的路徑,要使用get_template_directory,然後再加上自己要用的file的路徑,例如:要取的theme下的inc的demo.php要用

echo get_template_directory().'/inc/demo.php';

參考網址:
Function Reference/get template directory

2014年1月15日 星期三

Wordpress開發筆記-執行WordPress的Unit test的第一步

想寫WordPress plugin的unit test,還是先看看他們的範例好了,最好的當然是Wordpress開發時的Test case。查了下發現這篇Automated Testing照著做。

Step1: 先安裝好PHPUnit

可參考這篇PHPUnit學習筆記-PHPUnit安裝(使用PEAR)

Step2: 下載Wordpress正在測試的svn repository

下載的link: http://develop.svn.wordpress.org/trunk/
在Mac上svn是安裝好的,直接底下的指令即可
$cd (你要放置的目錄位置)
$svn co http://develop.svn.wordpress.org/trunk/ wordpress-develop
$cd wordpress-develop

Step3: 建個資料庫讓測試程式使用

這部分我用phpMyAdmin,隨便建個資料庫及它的名字,字集比對用utf8_general_ci

Step4: 修改wp-test-config.php

$cp wp-tests-config-sample.php wp-tests-config.php
$vim wp-tests-config.php

修改底下的資料
define( 'DB_NAME', '剛建好的資料庫' );
define( 'DB_USER', '使用者號' );
define( 'DB_PASSWORD', '密碼' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', 'utf8_general_ci' );

Step5: 執行PHPUnit Test cases啦~!

$phpunit
執行結果如下:

Installing...

Running as single site... To run multisite, use -c tests/phpunit/multisite.xml

Not running ajax tests... To execute these, use --group ajax.

PHPUnit 3.7.28 by Sebastian Bergmann.

Configuration read from /wordpress-develop/phpunit.xml.dist

.............................................................   61 / 1907 (  3%)
............SS...SSSSSS.............S...SS............SS.....  122 / 1907 (  6%)
...........S............S....................................  183 / 1907 (  9%)
........SSS.......SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS..S.......  244 / 1907 ( 12%)
.................S............................S.............S  305 / 1907 ( 15%)
...........................SSSSSSSSSS........................  366 / 1907 ( 19%)
..........S..................S.......I.......................  427 / 1907 ( 22%)
.................................SSSS.S..SSSSS...............  488 / 1907 ( 25%)
.............................................................  549 / 1907 ( 28%)
.............................................................  610 / 1907 ( 31%)
.............................................................  671 / 1907 ( 35%)
.............................................................  732 / 1907 ( 38%)
.............................................................  793 / 1907 ( 41%)
...........S.................................................  854 / 1907 ( 44%)
.............................................................  915 / 1907 ( 47%)
...................................SSSSSSSSSSSS..............  976 / 1907 ( 51%)
...............SSSSSS.........S.....S........................ 1037 / 1907 ( 54%)
................................S............................ 1098 / 1907 ( 57%)
.....................S................SSS.......S....SSSS.... 1159 / 1907 ( 60%)
.SS.......................................................... 1220 / 1907 ( 63%)
............................................................. 1281 / 1907 ( 67%)
............................................................. 1342 / 1907 ( 70%)
.....S.........S............................................. 1403 / 1907 ( 73%)
..............S.........................SS................... 1464 / 1907 ( 76%)
..............S..S........................................... 1525 / 1907 ( 79%)
...S...................SSS................................... 1586 / 1907 ( 83%)
............................................................. 1647 / 1907 ( 86%)
............................................................. 1708 / 1907 ( 89%)
............................................................. 1769 / 1907 ( 92%)
............................................................. 1830 / 1907 ( 95%)
.......SS...............SSSSSSSS

Time: 1.71 minutes, Memory: 101.00Mb

OK, but incomplete or skipped tests!
Tests: 1861, Assertions: 8619, Incomplete: 1, Skipped: 131.

看起來,WordPress是參考phpunit.xml.dist這個檔案,不過這檔案非必要。查了下Appendix C. The XML Configuration File,針對WordPress的phpunit.xml.dist稍微記錄一下。

phpunit.xml.dist的內容如下:

<phpunit

        bootstrap="tests/phpunit/includes/bootstrap.php"
        backupGlobals="false"
        colors="true"
        >
    <testsuites>
        <!-- Default test suite to run all tests -->
        <testsuite>
            <directory suffix=".php">tests/phpunit/tests</directory>
            <exclude>tests/phpunit/tests/actions/closures.php</exclude>
            <exclude>tests/phpunit/tests/image/editor.php</exclude>
            <exclude>tests/phpunit/tests/image/editor_gd.php</exclude>
            <exclude>tests/phpunit/tests/image/editor_imagick.php</exclude>
            <file phpVersion="5.3.0">tests/phpunit/tests/actions/closures.php</file>
            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor.php</file>
            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor_gd.php</file>
            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor_imagick.php</file>
        </testsuite>
    </testsuites>
    <groups>
        <exclude>
            <group>ajax</group>
        </exclude>
    </groups>
    <logging>
        <log type="junit" target="tests/phpunit/build/logs/junit.xml" logIncompleteSkipped="false"/>
    </logging>

</phpunit>


<phpunit>
是phpunit主要的根節點,記載可能的function。v是就指向phpunit的bootstrap.php的路徑,可以不用。

<testsuites>
就眾多的<testsuit>的集合,<testsuit>要放在裡頭

<testsuite>
實際的測試單元,會有name、directory、file等,例如這例子中唯一個testsuite

  • directory: 指定此testsuite的目錄路徑。
  • suffix: 例子中是指所有在此目錄下的php檔。
  • file: 指定某個php要執行,例如此例中closures.php,是php 5.3.0下才執行。
  • exclude: 指定某個php不執行
  • phpVersion是指定php的版本。
  • 其實還有phpVersionOperator,若設定phpVersion="5.3.0" phpVersionOperator=">=",表示php的版本要大於等於5.3.0


不過這篇主要是記錄怎樣執行WordPress的unit test的第一步,細節就再看Automated Testing吧。

PS. 也許可以幫忙寫寫WordPress的unit test。哈哈


參考網址:
Automated Testing
PHPUnit
Writing WordPress Plugin Unit Tests

Theme Unit Test
How To Join WPTRT
WP Test: Unit Testing Data for WordPress Themes and Plugins
phpunit.xml.dist設定說明:Appendix C. The XML Configuration File

Wordpress開發筆記-控制"控制台"上的資訊

Wordpress的後台總是個痛,畢竟他不是用來客製化成網站的(雖然可以),不過經過了這麼長的時間,後台多多少少可以做些修改,這次主要是
  • 將控制台中的快貼完全取消掉,連勾選的機會都要沒有
這還真的難倒了,重點是不知道要下什麼key,找半天看到這篇WP客製化 #4: 客製 WordPress 後台登入 LOGO、連結、版權訊息、後台首頁訊息資訊 (非外掛),寫的不錯,也有我要的答案。解法如下:

function wpc_dashboard_widgets() {
     global $wp_meta_boxes;
     unset($wp_meta_boxes['dashboard']['side']['core']['dashboard_quick_press']);
}
add_action('wp_dashboard_setup', 'wpc_dashboard_widgets');


仔細看看Dashboard Widgets API,原來做到可以再客製的地步了,例如其中提到

Add an Example Widget

Here's an example of a very basic dashboard widget. This code would go in one of your plugin's files, or in your theme's functions.php, for example.
/**
 * Add a widget to the dashboard.
 *
 * This function is hooked into the 'wp_dashboard_setup' action below.
 */
function example_add_dashboard_widgets() {

 wp_add_dashboard_widget(
                 'example_dashboard_widget',         // Widget slug.
                 'Example Dashboard Widget',         // Title.
                 'example_dashboard_widget_function' // Display function.
        ); 
}
add_action( 'wp_dashboard_setup', 'example_add_dashboard_widgets' );

/**
 * Create the function to output the contents of our Dashboard Widget.
 */
function example_dashboard_widget_function() {

 // Display whatever it is you want to show.
 echo "Hello World, I'm a great Dashboard Widget";
} 
也許用這方式可以不用登入後,為了跳過控制台還要轉址到其他地方,直接都拿掉,放入自己的資訊。


PS. 樂在設計的作者是個高一的學生,江山果然代有才人出,年紀只是多活的天數比較多,希望不會只是比他多活幾十年而已 XD

參考網址:
WP客製化 #4: 客製 WordPress 後台登入 LOGO、連結、版權訊息、後台首頁訊息資訊 (非外掛)
Dashboard Widgets API
Example Dashboard Widget

2014年1月1日 星期三

Wordpress開發筆記-加入Google自訂搜尋功能

2014年到,好好的清一清一堆草稿文章。從最近的下手。最近有需要用Google做站內搜尋,做法做個記錄。

Step1. 先進入後台,設定讓Google進入做搜尋索引,如下圖,將勾選拿掉


Step2.  至Google Custom Search page(登入你的Google帳號),會看到底下的管理介面,直接按新增(我這邊是已經有過一個,若沒有的話,會先進入下一張畫面)

Step3. 加入你的網站網址在第一個文字框,選擇語言,再按建立

Step4. 成功後,會看到底下的圖,按下”取得程式碼”,就可以取得javascript程式碼(後續要用到)

紅框部分就是

Step5. 在自己的wordpress的theme中新增search.php,在要顯示的div或html區塊中,加入紅框中的程式碼。

若要改外觀,可以在Step4中的控制台或回到https://www.google.com/cse/all中的列表點選剛才建的搜尋,即可看到下圖,可以設定搜尋的呈現外觀

Step 6. 再來要通知Google來做索引,要先驗證有這個網站的擁有權,先到網站管理員工具加入這個網址,按下新增網址,會跳出對話框請你加入,再加入你的網址,會再看到下一張圖

下載html到自己的網站根目錄,再點選驗證即可。

回到https://www.google.com/cse/all中,點選要search的網址,再點選設定 > 建立索引。有兩個方式可以要求做索引,這邊直接使用第一種:特定網址。加入網址(記得前面要有"-"號),再按下“立即建立索引”的按鈕即可。
要看到底下回應,才自成功

若要用sitemap,得再另外再寫一篇了,用那個還有一堆細節要調整。

參考網址:
Custom Search Element Control API
Creating Custom Search Engines programatically
如何在WordPress整合Google自訂搜尋?
Installing Google Custom Search Engine (CSE) On WordPress Site


新增Google CSE搜尋

Wordpress開發筆記-更動文章呈現數

最近用wordpress時有需求
  • 某自訂分類的文章在前台只能呈現9篇,其他則仍是後台設定的呈現篇數

查了半天,只要在theme中的functions.php(或其他需要處)加入

function modify_showcase_posts_per_page($wp_query) {
    if( get_query_var('showcase_cat_1') == null ) return;
    $wp_query->query_vars['posts_per_page'] = 9;
}
add_filter( 'pre_get_posts', 'modify_showcase_posts_per_page' );


註:pre_get_posts在Plugin API/Action Reference/pre get posts有提到,原文如下:

This hook is called after the query variable object is created, but before the actual query is run.
The pre_get_posts action gives developers access to the $query object by reference (any changes you make to $query are made directly to the original object - no return value is necessary).

2013年11月22日 星期五

Wordpress開發筆記-設定rewrite及htaccess

每次弄新的wordpress總會忘掉要做rewrite及htaccess,在這邊記一下

Apach config設定,vim /etc/httpd/conf/httpd.conf,然後加入
<Directory "{wordpress的路徑}">
AllowOverride All
</Directory>
restart apache server
/sbin/service httpd restart


至{wordpress的路徑}加入.htaccess
vim .htaccess,加入
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /{wordpress在www底下的路徑名}
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /{wordpress在www底下的路徑名}/index.php [L]
</IfModule>

參考網址:
htaccess
解决CentOS下.htaccess不起作用


2013年7月25日 星期四

CentOS筆記-安裝LAMP

前陣子用阿里雲的系統:CentOS 6.3,建好是一整個空的系統,連Apache、MySQL都沒有,真的空空如也,只能自己加上去,這邊記一下步驟:

安裝Apache http server
yum install httpd
service httpd start


修改/etc/httpd/conf/httpd.conf中<Directory "/var/www/html">下的Options
Options Indexes FollowSymLinks
改成Options -Indexes FollowSymLinks

安裝Mysql Server
yum install mysql-server
service mysqld start
/usr/bin/mysql_secure_installation


若要安裝5.5版,可以看CentOS筆記-CentOS 6.5 安裝MySQL 5.5

最後一個指令可以設定root密碼,將一些用不到的table及權限去除(link: MySQL Server 安裝後的設定

安裝PHP
yum install php php-mysql php-mysqli php-mbstring

重啟
chkconfig httpd on
chkconfig mysqld on


service httpd restart
service mysqld restart

這樣一個基本的LAMP就架起來了

架phpMyAdmin
我習慣用phpMyAdmin,所以再到PhpMyAdmin Home下載最新的tar檔,上傳到/var/www底下解開後,我是都會改目錄名稱為myadmin,用http://{hostname或ip}/myadmin,就可以用了,不過這次卻發生CentOS筆記-PHP Fatal error: Call to undefined function mb_detect_encoding()中提到的問題,就再依文中所提處理即可。

若有要安裝wordpress,是要再注意/etc/httpd/conf/httpd.conf中
LoadModule rewrite_module modules/mod_rewrite.so <-- 這個要有

檔案最後再加上底下幾行(主要是Rewrite那幾行)
AddType application/x-httpd-php .php .phtml

RewriteEngine on
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F]


記得再重啟http server

參考網址:
How to Install Linux, Apache, MySQL, PHP (LAMP) stack on CentOS 6
新安裝 CentOS 6.5 筆記

2013年6月30日 星期日

Wordpress開發筆記-拿掉後台管理區上那煩人的更新訊息

之前很多網站都用Wordpress開發,總會在最上面有個wordpress更新,給客戶看到總怪怪的,關掉它的理由:

  1. 客戶看到很怪也有會問那是什麼
  2. 總也有好奇心強的客戶就給它按下去,最後沒完沒了的收拾

最後結論:關掉它吧

但這好像是內定的,沒地方處理,找到這篇如何關閉 WordPress 管理後台的「版本更新」通知?,看起來除了改掉沒什麼方式,所以改掉方式如下:

Step1: 找到wp-admin/includes/update.php,最好先備份一下
Step2: 打開update.php,找到“echo "<div class='update-nag'>$msg</div>";”就comment掉

再進入後台就ok了。不過若是做了更新,要記得再做一次就是

參考網址:
如何關閉 WordPress 管理後台的「版本更新」通知?

2013年6月24日 星期一

Wordpress開發筆記-json_encode

有個需求
  1. 取得訊息,轉成json格式
  2. 當成post的meta存進資料庫

使用get方式丟上中文message後,本以為message有url encode過,再來用json_encode就沒問題了,卻發現取得時本來應該要是\U1234這樣的格式變成U1234的樣子存進資料庫,本以為是GET的中文出問題,但從$_GET取得時,是正常呈現。

找半天才發現是add_post_meta及update_post_meta會將“\”去除,若用json_encode過,會將中文都變成無法還原的亂碼。所以

Step1: 取得數值
$value = $_GET['test'];

Step2: 轉json格式
$value = json_encode($value);

Step3: url encode將“\”轉成ascii code
$value = urlencode($value);

Step4: 存進資料庫
add_post_meta(post_id,'message', $value);

記得取出時要decode回來,如:
$value = urldecode(get_post_meta(post_id,'message'));

參考網址:
解決json_encode中文UNICODE轉碼問題
解決PHP JSON 中文亂碼的問題