<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                面向對象的三大特性(封裝、繼承、多態),其中封裝是一個非常重要的特性。封裝隱藏了對象內部的細節和實現,使對象能夠集中而完整的描述并對應一個具體的事物,只提供對外的訪問接口,這樣可以在不改變接口的前提下改變實現細節,而且能使對象自我完備。除此之外,封裝還可以增強安全性和簡化編程。在面向對象的語言中一般是通過訪問控制來實現封裝的特性。PHP提供了public、protected及private三個層次訪問控制。這和其他面向對象的語言中對應的關鍵字語義一樣。這幾個關鍵字都用于修飾類的成員: - private 用于禁止除類本身以外(包括繼承也屬于非類本身)對成員的訪問,用于隱藏類的內部數據和實現。 - protected 用于禁止除本類以及繼承該類的類以外的任何訪問。同樣用于封裝類的實現,同時給予類一定的擴展能力,因為子類還是可以訪問到這些成員。 - public 最好理解,被public修飾的成員可以被任意的訪問。 > 如果沒有設置訪問控制關鍵字,則類的成員方法和成員變量會被設置成默認的 public。 這三個關鍵字在語法解析時分別對應三種訪問控制的標記: member_modifier: T_PUBLIC { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | T_PROTECTED { Z_LVAL($$.u.constant) = ZEND_ACC_PROTECTED; } | T_PRIVATE { Z_LVAL($$.u.constant) = ZEND_ACC_PRIVATE; } 這三種訪問控制的標記是PHP內核中定義的三個常量,在Zend/zend_compile.h中,其定義如下: #define ZEND_ACC_PUBLIC 0x100 #define ZEND_ACC_PROTECTED 0x200 #define ZEND_ACC_PRIVATE 0x400 #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) > 我們經常使用16進制的數字表標示狀態,例如上面的訪問控制常量, 0x100使用二進制表示就為 0001 0000 0000 0x200為0010 0000 0000 0x400為0100 0000 0000 我們通過二進制的某個位來表示特定的意義,至于為什么ZEND_ACC_PUBLIC這幾個常量后面多兩個0, 這是因為0x01和0x10已經被占用了,使用和其他不同意義的常量值不一樣的值可以避免誤用。 通過簡單的二進制&即可的除某個數值是否表示特定的意義,例如:某個常量為0011 0000 0000,這個數值和 0001 0000 0000 做&, 如果結果為0則說明這個位上的值不為1,在上面的例子中就是這個訪問控制不具有public的級別。 當然PHP中不允許使用多個訪問控制修飾符修飾同一個成員。這種處理方式在很多語言中都很常見。 在前面有提到當我們沒有給成員方法或成員變量設置訪問控制時,其默認值為public。與常規的訪問控制實現一樣,也是在語法解析階段進行的。 method_modifiers: /* empty */ { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | non_empty_member_modifiers { $$ = $1; if (!(Z_LVAL($$.u.constant) & ZEND_ACC_PPP_MASK)) { Z_LVAL($$.u.constant) |= ZEND_ACC_PUBLIC; } } ; 雖然是在語法解析時就已經設置了訪問控制,但其最終還是要存儲在相關結構中。在上面的語法解析過程中,訪問控制已經存儲在編譯節點中,在編譯具體的類成員時會傳遞給相關的結構。此變量會作為一個參數傳遞給生成中間代碼的函數。如在解析成員方法時,PHP內核是通過調用zend_do_begin_function_declaration函數實現,此函數的第五個參數表示訪問控制,在具體的代碼中, // ...省略 fn_flags = Z_LVAL(fn_flags_znode->u.constant); // ... 省略 ? op_array.fn_flags |= fn_flags; // ...省略 如此,就將訪問控制的相關參數傳遞給了將要執行的中間代碼。 假如我們先現在有下面一段代碼: class Tipi{ private static function t() { echo 1; } } ? Tipi::t(); 這個還是上一小節中我們說明靜態成員方法的示例,只是,這里我們將其訪問控制從public變成了private。執行這段代碼會報錯:Fatal error: Call to private method Tipi::t() from context '' in... 根據前一節的內容我們知道,如果要執行一個靜態成員變量需要先獲得類,再獲得類的方法,最后執行訪方法。而是否有訪問權限的檢測的實現過程在獲取類的方法過程中,即在zend_std_get_static_method函數中。此函數在獲取了類的方法后,會執行訪問控制的檢查過程。 if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) { //公有方法,可以訪問 } else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) { // 私有方法,報錯 } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { // 保護方法,報錯 } > 見前面有關訪問控制常量的討論,這是使用的是 fbc->op_array.fn_flags & ZEND_ACC_PUBLIC 而不是使用==來判斷訪問控制類型, 通過這種方式,op_array.fn_flags中可以保存不止訪問控制的信息,所以flag使用的是復數。 對于成員函數來說,其對于訪問控制存儲在函數結構體中的fn_flags字段中,不管是函數本身的common結構體中的fn_flags,還是函數包含所有中間代碼的代碼集合op_array中的fn_flags。 ## 訪問控制的小漏洞[]() 先看一個小例子吧: <?php ? class A { private $money = 10000; public function doSth($anotherA) { $anotherA->money = 10000000000; } ? public function getMoney() { return $this->money; } } ? $b = new A(); echo $b->getMoney(); // 10000 ? $a = new A(); $a->doSth($b); echo $b->getMoney(); // 10000000000; 在$a變量的doSth()方法中我們直接修改了$b變量的私有成員money,當然我們不太可能這樣寫代碼,從封裝的角度來看,這也是不應該的行為,從PHP實現的角度來看,這并不是一個功能,在其他語言中并不是這樣表現的。這也是PHP面向對象不純粹的表現之一。 下面我們從實現上面來看看是什么造就了這樣的行為。以下函數為驗證某個屬性能否被訪問的驗證方法: [static](http://www.php.net/static) int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) /* {{{ */ { switch (property_info->flags & ZEND_ACC_PPP_MASK) { case ZEND_ACC_PUBLIC: return 1; case ZEND_ACC_PROTECTED: return zend_check_protected(property_info->ce, EG(scope)); case ZEND_ACC_PRIVATE: if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) { return 1; } else { return 0; } break; } return 0; } 在doSth()方法中,我們要訪問$b對象的屬性money,這是Zend引擎檢查我們能否訪問$b對象的這個屬性,這是Zend贏取獲取$b對象的類,以及要訪問的屬性信息,首先要看看這個屬性是否為public,公開的話直接訪問就好了。如果是protected的則繼續調用zend_check_protected()函數檢查,因為涉及到該類的父類,這里不繼續跟這個函數了,看看是private的情況下是什么情況,在函數doSth()執行的時候,這時的EG(scope)指向的正是類A,ce變量值得就是變量$b的類,而$b的類就是類A,這樣檢查就判斷成功返回1,也就表示可以訪問。 至于成員函數的檢查規則類似,就留給讀者自己去探索了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看