เหตุใด PHP 5.2+ จึงไม่อนุญาตวิธีการคลาสแบบคงที่แบบนามธรรม


121

หลังจากเปิดใช้คำเตือนที่เข้มงวดใน PHP 5.2 ฉันเห็นคำเตือนมาตรฐานที่เข้มงวดมากมายจากโครงการที่เขียนขึ้นโดยไม่มีคำเตือนที่เข้มงวด:

มาตรฐานที่เข้มงวด : ฟังก์ชันคงที่โปรแกรม :: getSelectSQL () ไม่ควรเป็นนามธรรมใน Program.class.inc

ฟังก์ชันที่เป็นปัญหาเป็นของโปรแกรมคลาสพาเรนต์ที่เป็นนามธรรมและได้รับการประกาศว่าเป็นแบบคงที่เนื่องจากควรนำไปใช้ในคลาสย่อยเช่น TVProgram

ฉันพบการอ้างอิงถึงการเปลี่ยนแปลงนี้ที่นี่ :

ทิ้งฟังก์ชันคลาสคงที่ที่เป็นนามธรรม เนื่องจากการกำกับดูแล PHP 5.0.x และ 5.1.x จึงอนุญาตให้ใช้ฟังก์ชันคงที่ที่เป็นนามธรรมในคลาส ใน PHP 5.2.x มีเพียงอินเทอร์เฟซเท่านั้นที่สามารถมีได้

คำถามของฉันคือใครสามารถอธิบายได้อย่างชัดเจนว่าเหตุใดจึงไม่ควรมีฟังก์ชันคงที่ที่เป็นนามธรรมใน PHP


12
ผู้อ่านใหม่ควรทราบว่าข้อ จำกัด ที่ไม่ลงตัวนี้ได้ถูกลบออกใน PHP 7 แล้ว
Mark Amery

คำตอบ:


76

วิธีการคงที่เป็นของคลาสที่ประกาศ เมื่อขยายคลาสคุณอาจสร้างวิธีการแบบคงที่ที่มีชื่อเดียวกัน แต่คุณไม่ได้ใช้วิธีนามธรรมแบบคงที่

เช่นเดียวกันกับการขยายคลาสใด ๆ ด้วยวิธีการแบบคงที่ ถ้าคุณขยายคลาสนั้นและสร้างวิธีการแบบคงที่ของลายเซ็นเดียวกันคุณจะไม่ได้ลบล้างเมธอดแบบคงที่ของซุปเปอร์คลาส

แก้ไข (16 กันยายน 2552)
อัปเดตเกี่ยวกับเรื่องนี้ ใช้ PHP 5.3 ฉันเห็นว่าคงที่นามธรรมกลับมาดีหรือไม่ดี (ดูhttp://php.net/lsbสำหรับข้อมูลเพิ่มเติม)

CORRECTION (โดย philfreo)
abstract staticยังไม่ได้รับอนุญาตใน PHP 5.3 LSBเกี่ยวข้องกัน แต่ต่างกัน


3
ตกลงแล้วถ้าฉันต้องการบังคับให้ต้องใช้ฟังก์ชัน getSelectSQL () ในเด็กทั้งหมดที่ขยายคลาสนามธรรมของฉันล่ะ getSelectSQL () ในคลาสพาเรนต์ไม่มีเหตุผลที่ถูกต้อง แผนปฏิบัติการที่ดีที่สุดคืออะไร? เหตุผลที่ฉันเลือก Abstract Static คือโค้ดจะไม่คอมไพล์จนกว่าฉันจะติดตั้ง getSelectSQL () ในกลุ่มย่อยทั้งหมด
Artem Russakovskii

1
เป็นไปได้มากว่าคุณควรออกแบบสิ่งต่าง ๆ ใหม่เพื่อให้ getSelectSQL () เป็นนามธรรม / อินสแตนซ์ / วิธีการ ด้วยวิธีนั้น / อินสแตนซ์ / ของเด็กแต่ละคนจะมีวิธีการดังกล่าว
Matthew Flaschen

7
บทคัดย่อคงยังไม่ได้รับอนุญาตใน PHP 5.3 การผูกแบบคงที่ปลายไม่มีส่วนเกี่ยวข้องกับมัน ดูstackoverflow.com/questions/2859633
Artefacto

40
ในความคิดของฉันคำเตือนที่เข้มงวดนี้เป็นเพียงความโง่เขลาเนื่องจาก PHP มี "การผูกแบบคงที่" ซึ่งโดยธรรมชาติแล้วจะให้แนวคิดในการใช้วิธีการคงที่ราวกับว่าคลาสนั้นเป็นวัตถุ (เช่นพูดในทับทิม) ซึ่งนำไปสู่การโอเวอร์โหลดวิธีคงที่และabstract staticอาจเป็นประโยชน์ในกรณีนี้
dmitry

4
คำตอบนี้ไม่ถูกต้อง "ยังไม่ได้รับอนุญาต" หมายความว่าคุณจะได้รับคำเตือนระดับ E_STRICT อย่างน้อยใน 5.3+ คุณสามารถสร้างฟังก์ชันคงที่ที่เป็นนามธรรมได้อย่างสมบูรณ์นำไปใช้ในคลาสเพิ่มเติมจากนั้นอ้างอิงถึงพวกเขาผ่านคำหลัก static :: เห็นได้ชัดว่าเวอร์ชันคงที่ของคลาสแม่ยังคงอยู่และไม่สามารถเรียกได้โดยตรง (ผ่าน self :: หรือ static :: ภายในคลาสนั้น) เนื่องจากเป็นนามธรรมและจะเกิดข้อผิดพลาดร้ายแรงราวกับว่าคุณเรียกฟังก์ชันนามธรรมที่ไม่คงที่ปกติ ฟังก์ชั่นนี้มีประโยชน์ฉันเห็นด้วยกับความรู้สึกของ @dmitry ต่อเอฟเฟกต์นั้น
ahoffner

79

เป็นเรื่องยาวที่น่าเศร้า

เมื่อ PHP 5.2 เปิดตัวคำเตือนนี้เป็นครั้งแรกการผูกแบบคงที่ล่าช้ายังไม่ได้อยู่ในภาษา ในกรณีที่คุณไม่คุ้นเคยกับการผูกแบบคงที่ในช่วงปลายโปรดทราบว่ารหัสแบบนี้ไม่ได้ผลอย่างที่คุณคาดหวัง:

<?php

abstract class ParentClass {
    static function foo() {
        echo "I'm gonna do bar()";
        self::bar();
    }

    abstract static function bar();
}

class ChildClass extends ParentClass {
    static function bar() {
        echo "Hello, World!";
    }
}

ChildClass::foo();

ยกเว้นคำเตือนโหมดเข้มงวดโค้ดด้านบนใช้ไม่ได้ การself::bar()เรียกในfoo()อย่างชัดเจนหมายถึงbar()วิธีการParentClassแม้ว่าfoo()จะถูกเรียกว่าเป็นวิธีการChildClassก็ตาม หากคุณพยายามรันโค้ดนี้โดยปิดโหมดเข้มงวดคุณจะเห็น " PHP Fatal error: Can not call abstract method ParentClass :: bar () "

ด้วยวิธีนี้วิธีการคงที่แบบนามธรรมใน PHP 5.2 จึงไร้ประโยชน์ จุดทั้งหมดของการใช้วิธีนามธรรมคือที่คุณสามารถเขียนโค้ดที่เรียกวิธีโดยไม่ทราบว่าสิ่งที่ดำเนินการก็จะถูกเรียก - แล้วให้การใช้งานที่แตกต่างกันในชั้นเรียนของเด็กที่แตกต่างกัน แต่เนื่องจาก PHP 5.2 ไม่มีวิธีที่สะอาดในการเขียนเมธอดของคลาสพาเรนต์ที่เรียกเมธอดแบบคงที่ของคลาสลูกที่เรียกว่าการใช้เมธอดแบบคงที่นามธรรมนี้จึงไม่สามารถทำได้ ดังนั้นการใช้งานใด ๆabstract staticใน PHP 5.2 จึงเป็นโค้ดที่ไม่ถูกต้องอาจได้รับแรงบันดาลใจจากความเข้าใจผิดว่าselfคำหลักทำงานอย่างไร มันสมเหตุสมผลอย่างยิ่งที่จะต้องเตือนเรื่องนี้

แต่จากนั้น PHP 5.3 ก็เข้ามาพร้อมกับความสามารถในการอ้างถึงคลาสที่เมธอดถูกเรียกใช้ผ่านstaticคีย์เวิร์ด (ไม่เหมือนกับselfคีย์เวิร์ดซึ่งหมายถึงคลาสที่กำหนดเมธอดไว้เสมอ) หากคุณเปลี่ยนself::bar()เป็นstatic::bar()ในตัวอย่างของฉันข้างต้นมันทำงานได้ดีใน PHP 5.3 ขึ้นไป คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการselfเทียบกับstaticที่ตัวเองใหม่กับแบบคงที่ใหม่

เมื่อเพิ่มคีย์เวิร์ดแบบคงที่อาร์กิวเมนต์ที่ชัดเจนสำหรับการabstract staticส่งคำเตือนก็หายไป จุดประสงค์หลักของการโยงแบบคงที่ในช่วงปลายคือเพื่อให้เมธอดที่กำหนดไว้ในคลาสพาเรนต์เรียกเมธอดแบบคงที่ซึ่งจะกำหนดไว้ในคลาสย่อย การอนุญาตให้วิธีการคงที่แบบนามธรรมดูสมเหตุสมผลและสอดคล้องกันเมื่อมีการเชื่อมแบบคงที่ในช่วงปลาย

คุณยังสามารถฉันเดาว่าสร้างกรณีเพื่อรักษาคำเตือน ตัวอย่างเช่นคุณสามารถโต้แย้งได้ว่าเนื่องจาก PHP ช่วยให้คุณสามารถเรียกวิธีการแบบคงที่ของคลาสนามธรรมได้ในตัวอย่างของฉันด้านบน (แม้ว่าจะแก้ไขโดยการแทนที่selfด้วยstatic) คุณกำลังเปิดเผยวิธีการสาธารณะParentClass::foo()ที่เสียและคุณไม่ต้องการจริงๆ เปิดเผย การใช้คลาสที่ไม่คงที่นั่นคือการสร้างวิธีการอินสแตนซ์เมธอดทั้งหมดและทำให้เด็ก ๆParentClassทั้งหมดเป็นซิงเกิลตันหรืออะไรบางอย่าง - จะแก้ปัญหานี้ได้เนื่องจากParentClassเป็นนามธรรมไม่สามารถสร้างอินสแตนซ์ได้ดังนั้นวิธีการอินสแตนซ์จึงไม่สามารถทำได้ ถูกเรียก. ฉันคิดว่าข้อโต้แย้งนี้อ่อนแอ (เพราะฉันคิดว่าการเปิดเผยParentClass::foo() ไม่ใช่เรื่องใหญ่และการใช้เสื้อกล้ามแทนที่จะเป็นคลาสแบบคงที่มักจะใช้คำฟุ่มเฟือยและน่าเกลียดโดยไม่จำเป็น) แต่คุณอาจไม่เห็นด้วยพอสมควร - เป็นการเรียกแบบส่วนตัว

จากข้อโต้แย้งนี้นักพัฒนา PHP ยังคงคำเตือนเป็นภาษาใช่ไหม?

เอ่อ, ไม่ตรง

รายงานข้อบกพร่องของ PHP 53081 ที่เชื่อมโยงด้านบนเรียกร้องให้ยกเลิกการแจ้งเตือนเนื่องจากการเพิ่มstatic::foo()โครงสร้างทำให้วิธีการคงที่เป็นนามธรรมมีความสมเหตุสมผลและเป็นประโยชน์ Rasmus Lerdorf (ผู้สร้าง PHP) เริ่มต้นด้วยการติดฉลากคำขอว่าเป็นของปลอมและใช้เหตุผลที่ไม่ดีเป็นห่วงโซ่ยาวเพื่อพยายามพิสูจน์คำเตือน จากนั้นในที่สุดการแลกเปลี่ยนนี้จะเกิดขึ้น:

Giorgio

ฉันรู้ แต่:

abstract class cA
{
      //static function A(){self::B();} error, undefined method
      static function A(){static::B();} // good
      abstract static function B();
}

class cB extends cA
{
    static function B(){echo "ok";}
}

cB::A();

ราสมูส

ถูกต้องนั่นคือวิธีการทำงาน

Giorgio

แต่ไม่อนุญาต :(

ราสมูส

อะไรไม่ได้รับอนุญาต

abstract class cA {
      static function A(){static::B();}
      abstract static function B();
}

class cB extends cA {
    static function B(){echo "ok";}
}

cB::A();

ใช้งานได้ดี เห็นได้ชัดว่าคุณไม่สามารถเรียกตัวเองว่า :: B () ได้ แต่คงที่ :: B () ก็ใช้ได้

คำกล่าวอ้างของ Rasmus ว่าโค้ดในตัวอย่าง "ใช้ได้ดี" เป็นเท็จ ดังที่คุณทราบระบบจะแสดงคำเตือนโหมดเข้มงวด ฉันเดาว่าเขากำลังทดสอบโดยไม่เปิดโหมดเข้มงวด อย่างไรก็ตาม Rasmus ที่สับสนปล่อยให้คำขอปิดโดยไม่ถูกต้องว่าเป็น "ปลอม"

และนั่นคือสาเหตุที่คำเตือนยังคงเป็นภาษา นี่อาจไม่ใช่คำอธิบายที่น่าพึงพอใจทั้งหมด - คุณอาจมาที่นี่โดยหวังว่าจะมีคำเตือนที่สมเหตุสมผล น่าเสียดายที่ในโลกแห่งความเป็นจริงบางครั้งการเลือกเกิดจากความผิดพลาดทางโลกและการใช้เหตุผลที่ไม่ดีแทนที่จะมาจากการตัดสินใจอย่างมีเหตุผล นี่เป็นเพียงหนึ่งในครั้งนั้น

โชคดีที่นับถือ Nikita โปปอฟได้ลบออกคำเตือนจากภาษา PHP 7 เป็นส่วนหนึ่งของPHP RFC: ประกาศจัดกลุ่มใหม่ ในที่สุดสติก็มีชัยแล้วและเมื่อ PHP 7 เปิดตัวเราทุกคนก็สามารถใช้งานได้อย่างมีความสุขabstract staticโดยไม่ได้รับคำเตือนที่ไร้สาระนี้


70

มีวิธีแก้ไขที่ง่ายมากสำหรับปัญหานี้ซึ่งทำให้เข้าใจได้จริงจากมุมมองของการออกแบบ ดังที่โจนาธานเขียน:

เช่นเดียวกันกับการขยายคลาสใด ๆ ด้วยวิธีการแบบคงที่ ถ้าคุณขยายคลาสนั้นและสร้างวิธีการแบบคงที่ของลายเซ็นเดียวกันคุณจะไม่ได้ลบล้างเมธอดแบบคงที่ของซุปเปอร์คลาส

ดังนั้นในฐานะที่เป็นงานรอบตัวคุณสามารถทำได้:

<?php
abstract class MyFoo implements iMyFoo {

    public static final function factory($type, $someData) {
        // don't forget checking and do whatever else you would
        // like to do inside a factory method
        $class = get_called_class()."_".$type;
        $inst = $class::getInstance($someData);
        return $inst;
    }
}


interface iMyFoo {
    static function factory($type, $someData);
    static function getInstance();
    function getSomeData();
}
?>

และตอนนี้คุณบังคับให้คลาสย่อย MyFoo ใช้เมธอดแบบคงที่ getInstance และเมธอด getSomeData สาธารณะ และถ้าคุณไม่ได้ subclass MyFoo คุณยังสามารถใช้ iMyFoo เพื่อสร้างคลาสที่มีฟังก์ชันการทำงานที่คล้ายกันได้


2
มันเป็นไปได้ด้วยรูปแบบนี้จะทำให้ฟังก์ชั่นการป้องกัน เมื่อฉันทำมันหมายถึงคลาสที่ขยายคำเตือน MyFoo throw ที่ getInstance ต้องเป็นสาธารณะ และคุณไม่สามารถป้องกันไว้ในนิยามอินเตอร์เฟสได้
artfulrobot

3
บางครั้งstatic::อาจมีประโยชน์
seyed

3
ใช้ไม่ได้กับลักษณะ ถ้า Traits เท่านั้นที่สามารถมีabstract staticวิธีการได้โดยไม่ต้องใช้ PHP bitching ....
Rudie

1
การใช้อินเทอร์เฟซน่าจะเป็นทางออกที่ดีที่สุดที่นี่ +1
Juan Carlos Coto

นี่เป็นสิ่งที่สง่างามมากเนื่องจากความเรียบง่ายที่แท้จริง +1
G. Stewart

12

ฉันรู้ว่ามันเก่า แต่ ....

ทำไมไม่เพียงแค่โยนข้อยกเว้นไปยังวิธีการคงที่ของคลาสพาเรนต์ด้วยวิธีนั้นถ้าคุณไม่ลบล้างมันจะเกิดข้อยกเว้น


1
นั่นไม่ได้ช่วยอะไรข้อยกเว้นจะเกิดขึ้นเมื่อเรียกใช้วิธีการคงที่ - ในเวลาเดียวกันข้อผิดพลาด "ไม่มีวิธีการ" จะเกิดขึ้นหากคุณไม่ได้ลบล้าง
บีที

3
@BT ฉันหมายถึงอย่าประกาศวิธีนามธรรมนำไปใช้ แต่เพียงแค่โยนข้อยกเว้นเมื่อมีการเรียกซึ่งหมายความว่าจะไม่โยนหากถูกแทนที่
Petah

นี่ดูเหมือนจะเป็นทางออกที่สง่างามที่สุด
Alex S

ดีกว่าที่จะเห็นบางสิ่งเช่นนี้ในเวลารวบรวมมากกว่าเวลาทำงาน พบปัญหาได้ง่ายขึ้นก่อนที่จะดำเนินการผลิตเพราะคุณต้องโหลดไฟล์เท่านั้นไม่ต้องรันโค้ดเพื่อดูว่าไฟล์นั้นไม่ดีหรือไม่เป็นไปตามข้อกำหนด
Rahly

4

ฉันจะยืนยันว่าคลาส / อินเทอร์เฟซนามธรรมอาจถูกมองว่าเป็นสัญญาระหว่างโปรแกรมเมอร์ เกี่ยวข้องมากขึ้นว่าสิ่งต่างๆควรมีลักษณะ / พฤติกรรมอย่างไรและไม่ใช้ฟังก์ชันการทำงานจริง ดังที่เห็นใน php5.0 และ 5.1.x ไม่ใช่กฎธรรมชาติที่ป้องกันไม่ให้นักพัฒนา php ทำ แต่ต้องการที่จะไปพร้อมกับรูปแบบการออกแบบ OO อื่น ๆ ในภาษาอื่น โดยพื้นฐานแล้วแนวคิดเหล่านี้พยายามป้องกันพฤติกรรมที่ไม่คาดคิดหากคุ้นเคยกับภาษาอื่นอยู่แล้ว


แม้ว่าจะไม่เกี่ยวข้องกับ php แต่ก็เป็นอีกคำอธิบายที่ดี: stackoverflow.com/questions/3284/…
merkuro

3
พระเจ้า! คนสองคนที่โหวตให้คุณไม่อยู่ในใจ! นี่คือคำตอบที่ลึกซึ้งที่สุดในชุดข้อความนี้
Theodore R.Smith

5
@ TheodoreR.Smith Insightful? มันมีข้อผิดพลาดและแทบจะไม่สอดคล้องกัน การอ้างว่าคลาสนามธรรม"ไม่ใช้ฟังก์ชันการทำงานจริง"นั้นไม่จำเป็นต้องเป็นจริงและนั่นคือประเด็นทั้งหมดที่มีอยู่นอกเหนือจากอินเทอร์เฟซ ในการอ้างว่า"ไม่ใช่กฎธรรมชาติที่ป้องกันไม่ให้นักพัฒนา php ทำ"ฉันไม่รู้ว่า"มัน"คืออะไร และในการอ้างว่า"โดยพื้นฐานแล้วความคิดเหล่านี้พยายามป้องกันพฤติกรรมที่ไม่คาดคิด"ฉันไม่รู้ว่า "ความคิดเหล่านี้" หรือ "พฤติกรรมที่ไม่คาดคิด" ที่อาจเกิดขึ้นคืออะไร ไม่ว่าคุณจะดึงข้อมูลเชิงลึกอะไรออกมามันก็หายไปกับฉัน
Mark Amery

2

ฉันไม่เห็นเหตุผลใด ๆ ที่จะห้ามฟังก์ชันนามธรรมคงที่ ข้อโต้แย้งที่ดีที่สุดที่ไม่มีเหตุผลที่จะห้ามพวกเขาคือพวกเขาได้รับอนุญาตใน Java คำถามคือ - เป็นไปได้ในทางเทคนิคหรือไม่? - ใช่เนื่องจากมีอยู่ใน PHP 5.2 และมีอยู่ใน Java ดังนั้นจึงสามารถทำได้ เราควรทำหรือไม่ - เข้าท่าไหม? ใช่. มันสมเหตุสมผลที่จะนำส่วนหนึ่งของคลาสไปใช้และปล่อยให้อีกส่วนหนึ่งของคลาสให้กับผู้ใช้ มันสมเหตุสมผลในฟังก์ชั่นที่ไม่คงที่ทำไมจึงไม่เหมาะสมสำหรับฟังก์ชันคงที่? การใช้ฟังก์ชันคงที่อย่างหนึ่งคือคลาสที่ต้องมีไม่เกินหนึ่งอินสแตนซ์ (ซิงเกิลตัน) ตัวอย่างเช่นเอ็นจินการเข้ารหัส ไม่จำเป็นต้องมีอยู่ในหลาย ๆ กรณีและมีเหตุผลในการป้องกันเช่นคุณต้องปกป้องหน่วยความจำเพียงส่วนเดียวจากผู้บุกรุก ดังนั้นจึงเหมาะสมอย่างยิ่งที่จะใช้ส่วนหนึ่งของเครื่องยนต์และปล่อยให้อัลกอริทึมการเข้ารหัสแก่ผู้ใช้ นี่เป็นเพียงตัวอย่างหนึ่งเท่านั้น หากคุณคุ้นเคยกับการใช้ฟังก์ชันคงที่คุณจะพบอีกมากมาย


3
ประเด็นของคุณเกี่ยวกับวิธีการคงที่แบบนามธรรมที่มีอยู่ใน 5.2 นั้นทำให้เข้าใจผิดอย่างมาก ประการแรกคำเตือนโหมดเข้มงวดที่ห้ามไม่ให้มีการแนะนำใน 5.2; คุณทำให้เสียงเหมือนใน 5.2 ได้รับอนุญาต ประการที่สองใน 5.2 พวกเขาไม่สามารถใช้"เพื่อนำส่วนหนึ่งของคลาสไปใช้งานได้อย่างง่ายดายและปล่อยให้ส่วนอื่นของคลาสให้กับผู้ใช้"เนื่องจากยังไม่มีการผูกแบบคงที่ในช่วงปลาย
Mark Amery

0

ใน php 5.4+ ใช้ลักษณะ:

trait StaticExample {
    public static function instance () {
    return new self;
    }
}

และในชั้นเรียนของคุณกล่าวถึงการขอทาน:

use StaticExample;

1
ฉันสามารถใส่abstract public static function get_table_name();ลักษณะและใช้ลักษณะนั้นในคลาสนามธรรมของฉันโดยไม่มีคำเตือน E_STRICT อีกต่อไป! สิ่งนี้ยังคงบังคับใช้การกำหนดวิธีการคงที่ในเด็กตามที่ฉันหวังไว้ Fantastic!
Programster

-1

ตรวจสอบปัญหา 'Late Static Binding' ของ PHP หากคุณใช้วิธีการแบบคงที่ในคลาสนามธรรมคุณอาจจะต้องพบเจอกับมันในไม่ช้าก็เร็ว มันสมเหตุสมผลแล้วที่คำเตือนที่เข้มงวดกำลังบอกให้คุณหลีกเลี่ยงการใช้คุณสมบัติภาษาที่ไม่เหมาะสม


4
ฉันคิดว่าเขาหมายถึงคำเตือน "มาตรฐานที่เข้มงวด"
Jacob Hume

2
"ปัญหา" ใดที่คุณอ้างว่าการผูกแบบคงที่ในช่วงปลายมี คุณยืนยันว่าพวกเขา "เสีย" ซึ่งเป็นการกล่าวอ้างที่ชัดเจนโดยไม่มีหลักฐานหรือคำอธิบายใด ๆ ฟีเจอร์นี้ใช้งานได้ดีสำหรับฉันเสมอและฉันคิดว่าโพสต์นี้ไม่มีสาระ
Mark Amery
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.