首頁  >  文章  >  後端開發  >  php define常數詳解

php define常數詳解

WBOY
WBOY原創
2016-07-25 08:53:251666瀏覽
  1. class A {
  2. public function __toString() {
  3. return 'bar';
  4. }
  5. }
  6. $a = new A();
  7. define('foo', $a);
  8. echo foo;
  9. // 輸出bar
複製程式碼

複製程式碼
    php中的define究竟是如何實現的:
  1. ZEND_FUNCTION(define)

  2. {
  3. char *name;
  4. int name_len; zval *val_free = NULL;
  5. zend_bool non_cs = 0;
  6. int case_sensitive = CONST_CS;
  7. zend_constant c;
  8. // 接收3個參數, string,zval,bool

  9. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) {
  10. return; 🎜>

    // 是否大小寫敏感

  11. if(non_cs) {
  12. case_sensitive = 0;
  13. }
  14. // 如果define類別常數,則報錯

  15. if (zend_memnstr(name, "::", sizeof("::") - 1, name name_len)) {
  16. zend_error(E_WARNING, "Class constants cannot be defined or redefined"); }
  17. // 取得真正的值,用val儲存

  18. repeat:
  19. switch (Z_TYPE_P(val)) {
  20. case IS_LONG:
  21. case IS_DOUBLE:
  22. case IS_STRING:
  23. case IS_BOOL:
  24. case IS_RESOURCE:
  25. case IS_NULL:
  26. break;
  27. case IS_OBJECT IS_NULL:
  28. break;
  29. copase IS_OBJECTECTget) {
  30. val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
  31. goto repeat;
  32. } else if (Z_OBJ_castP(Z_OBJ_objval)ect)> ) {
  33. ALLOC_INIT_ZVAL(val_free);
  34. if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) {
  35. break
  36. ; }
  37. }
  38. }
  39. /* no break */
  40. default:
  41. zend_error(E_WARNING,"Constants may only evaluate to scalar values");
  42. if ( zval_ptr_dtor(&val_free);
  43. }
  44. RETURN_FALSE;
  45. }
  46. // 建構常數
  47. c.value = *val
  48. if (val_free) {
  49. zval_ptr_dtor(&val_free);
  50. }
  51. c.flags = case_sensitive; /* non persistent */ // 若大小寫不敏感,則為0,則為0,則為0,則為1
  52. c.name = zend_strndup(name, name_len);
  53. c.name_len = name_len 1;
  54. c.module_number = PHP_USER_CONSTANT; // 標註非內核常數,而是使用者定義的量
  55. // 註冊常數
  56. if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {
  57. RETURN_TRUE;
  58. } else {
  59. RETURN_FALSE;
  60. 複製程式碼
注意以repeat開始的一段循環,也用到了goto語句T_T 這段程式碼的作用為: 對於int,float,string,bool,resource,null,則實際定義的常數時直接使用這些值 對於object,則需要將object轉換成上述6個類型之一(如果轉型之後依然是object,則繼續轉型) 如何將object成6個類型之一呢?從程式碼上看有2種手段:

if (Z_OBJ_HT_P(val)->get) {
val_free = val = Z_OBJ_HT_P(val)->get(val TS goto repeat;
    }
  1. // __toString()方法會在cast_object中被呼叫
  2. else if (Z_OBJ_HT_P(val)->cast_object) {
  3. ALLOC_INIT_ZVAL(val_freeIT_ZVAL(val_free); (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS)
  4. {
  5. val = val_free;
  6. break;
  7. }
  8. }
  9. break;
  10. }
  11. 複製程式碼
  12. 1,Z_OBJ_HT_P(val)->get ,宏展開之後為(*val).value.obj.handlers->get

    2,Z_OBJ_HT_P(val)->cast_object,巨集展開之後為(*val).value.obj.handlers->cast_object

    handlers是一個包含許多函數指標的結構體,具體定義參見_zend_object_handlers 。此結構體中的函數指標皆用於操作object,例如讀取/修改物件屬性、取得/呼叫物件方法等等...get和cast_object也是其中之一。

    對於一般的對象,php提供了標準的cast_object函數zend_std_cast_object_tostring,程式碼位於php-src/zend/zend-object-handlers.c:

    1. ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type RM =DC)* {

    2. zval *retval;
    3. zend_class_entry *ce;
    4. switch (type) {

    5. case IS_STRING:
    6. ce = Z_OBJCE_P(readobj);
    7. // 如果使用者的class中定義了__toString,則嘗試呼叫
    8. if (ce->__tostring &&
    9. (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
    10. ……
    11. }
    return FAILURE;
    …… } return FAILURE;
    }

    }

    return FAILURE;

    }

    複製程式碼

    從上述具體實作來看,預設的cast_object就是去尋找class中的__tostring方法然後呼叫...

    回到剛開始的例子,define('foo', $a) ,由於$a是A的實例,而class A中定義了__toString,因此實際上foo常數等於toString的回傳值bar。
      ps:繼續挖掘一點小細節.
    1. 1,define有回傳值 通常我們定義常數直接寫成:define('foo', 123); 不過從define的實作來看,它是有回傳值的。根據手冊上的描述:
    2. 成功時回傳 TRUE, 或在失敗時回傳 FALSE。
    3. 什麼情況下define會失敗呢? 舉個例子:

    define('PHP_INT_MAX', 1); // 回傳FALSE

    define(' FOO', 1); // 回傳TRUE

    define('FOO', 2); // 回傳FALSE

    複製程式碼
    1. 上麵程式碼包含了兩種情況,一是我們嘗試重新定義php核心的預設常數,例如PHP_INT_MAX,這顯然會失敗。第二種情況是我們曾經在程式碼的某個位置定義過了一個常數FOO,然後又在接下來的程式中再次定義它,這也會造成失敗。因此,在編碼時最好將所有需要定義的常數寫在一起,以免造成name重複。
    2,常數名沒有限制 再回顧define的實現,其中僅僅判斷name是否為XXX::YYY這種形式。
    換句話說,define幾乎對其name不做任何要求,當然也不需要name是一個合法的php變數名稱。因此,我們可以讓define的常數取一些稀奇古怪的名稱。例如:

    define('>_echo >_
  13. echo >_
複製程式碼
不過如果定義了這樣的常數,是沒法直接使用的,會報語法錯誤。正確的使用方法如下:
define('>_echo constant('>_

複製程式碼
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn