วิธีการเริ่มต้นตัวแปรคงที่


207

ฉันมีรหัสนี้:

private static $dates = array(
  'start' => mktime( 0,  0,  0,  7, 30, 2009),  // Start date
  'end'   => mktime( 0,  0,  0,  8,  2, 2009),  // End date
  'close' => mktime(23, 59, 59,  7, 20, 2009),  // Date when registration closes
  'early' => mktime( 0,  0,  0,  3, 19, 2009),  // Date when early bird discount ends
);

ซึ่งทำให้ฉันข้อผิดพลาดต่อไปนี้:

ข้อผิดพลาดในการแยกวิเคราะห์: ข้อผิดพลาดทางไวยากรณ์, ไม่คาดคิด '(', คาดหวัง ')' ใน /home/user/Sites/site/registration/inc/registration.class.inc ที่บรรทัด 19

ดังนั้นฉันเดาว่าฉันกำลังทำอะไรผิด ... แต่ฉันจะทำสิ่งนี้ได้อย่างไรถ้าไม่ทำอย่างนั้น? ถ้าฉันเปลี่ยนสิ่ง mktime ด้วยสตริงปกติมันใช้งานได้ ดังนั้นฉันรู้ว่าฉันสามารถทำมันได้เรียงลำดับของเช่นว่า ..

ใครมีพอยน์เตอร์บ้าง?



2
คำตอบแรกคือโหวตมากกว่า ดูstackoverflow.com/a/4470002/632951
Pacerier

1
@Pierier ฉันไม่คิดอย่างนั้น คำตอบ # 2มีค่าใช้จ่ายจำนวนมากเมื่อเทียบกับคำตอบ # 1
Kontrollfreak

2
@Pacerier ถาม 10 คนไม่มีใครชอบ
บัฟฟาโล

คำตอบ:


345

PHP ไม่สามารถแยกวิเคราะห์การแสดงออกที่ไม่สำคัญในการเริ่มต้น

ฉันชอบที่จะหลีกเลี่ยงปัญหานี้โดยการเพิ่มรหัสทันทีหลังจากนิยามของคลาส:

class Foo {
  static $bar;
}
Foo::$bar = array(…);

หรือ

class Foo {
  private static $bar;
  static function init()
  {
    self::$bar = array(…);
  }
}
Foo::init();

PHP 5.6สามารถจัดการกับการแสดงออกบางอย่างในขณะนี้

/* For Abstract classes */
abstract class Foo{
    private static function bar(){
        static $bar = null;
        if ($bar == null)
            bar = array(...);
        return $bar;
    }
    /* use where necessary */
    self::bar();
}

135
ฉันรัก PHP แต่บางครั้งก็แปลกจริงๆ
Marco Demaio

6
ฉันรู้ว่านี่เก่า แต่ฉันก็ใช้วิธีนี้เช่นกัน อย่างไรก็ตามฉันพบว่าบางครั้ง Foo :: init () ไม่ได้ถูกเรียก ฉันไม่สามารถติดตามสาเหตุได้ แต่ต้องการทำให้ทุกคนรู้
lucifurious

1
@porneL วิธีแรกจะใช้ไม่ได้เพราะคุณไม่สามารถเข้าถึงตัวแปรส่วนตัวได้ วิธีที่สองใช้งานได้ แต่มันบังคับให้เราinitเปิดเผยต่อสาธารณะซึ่งน่าเกลียด ทางออกที่ดีกว่าคืออะไร
Pacerier

2
@Pierier วิธีแรกใช้ทรัพย์สินสาธารณะด้วยเหตุผล AFAIK ไม่มีทางออกที่ดีกว่าใน PHP ในขณะนี้ (คำตอบของ Tjeerd Visser นั้นไม่เลวเลย) การซ่อนแฮ็กในตัวโหลดคลาสไม่ได้ทำให้มันหายไปบังคับให้มีการสืบทอดที่ผิดพลาดและมันเป็นความฉลาดที่สามารถทำลายได้โดยไม่คาดคิด (เช่นเมื่อไฟล์ต้องการ () d อย่างชัดเจน)
Kornel

1
@porneL Simple array ทำงานให้ฉันใน PHP 5.6.x แม้ว่าจะไม่ได้กล่าวถึงใน RFC ตัวอย่าง:class Foo {public static $bar = array(3 * 4, "b" => 7 + 8);} var_dump(Foo::$bar);
ปาง

32

หากคุณมีการควบคุมการโหลดคลาสคุณสามารถทำการเริ่มต้นแบบคงที่จากที่นั่น

ตัวอย่าง:

class MyClass { public static function static_init() { } }

ในตัวโหลดคลาสของคุณให้ทำดังนี้:

include($path . $klass . PHP_EXT);
if(method_exists($klass, 'static_init')) { $klass::staticInit() }

โซลูชันที่มีน้ำหนักมากขึ้นคือใช้อินเทอร์เฟซกับ ReflectionClass:

interface StaticInit { public static function staticInit() { } }
class MyClass implements StaticInit { public static function staticInit() { } }

ในตัวโหลดคลาสของคุณให้ทำดังนี้:

$rc = new ReflectionClass($klass);
if(in_array('StaticInit', $rc->getInterfaceNames())) { $klass::staticInit() }

นี่เป็นอะไรที่ค่อนข้างคล้ายกับตัวสร้างแบบคงที่ใน c # ฉันใช้สิ่งที่คล้ายกันมานานและใช้งานได้ดีมาก
กริช

@EmanuelLandeholm ดังนั้นวิธีหนึ่งจึงเร็วกว่าหรือสองวิธีเร็วกว่ากัน
Pacerier

@Kris นั่นไม่ใช่เรื่องบังเอิญ ฉันได้รับแรงบันดาลใจจาก c # ในขณะตอบรับ
Emanuel Landeholm

1
@Pierier ฉันไม่มีหลักฐาน แต่ฉันสงสัยว่า ReflectionClass () อาจมีค่าใช้จ่ายมากกว่า OTOH วิธีแรกสร้างข้อสันนิษฐานที่ค่อนข้างอันตรายว่าวิธีการใด ๆ ที่เรียกว่า "static_init" ในคลาสใดก็ตามที่โหลดโดยตัวโหลดคลาสเป็นตัวเริ่มต้นคงที่ สิ่งนี้อาจนำไปสู่การติดตามข้อบกพร่องบางอย่างเช่น กับชั้นเรียนของบุคคลที่สาม
Emanuel Landeholm

23

แทนที่จะหาวิธีที่จะทำให้ตัวแปรคงที่ทำงานได้ฉันต้องการสร้างฟังก์ชัน getter มีประโยชน์ด้วยหากคุณต้องการอาร์เรย์ที่อยู่ในคลาสที่เฉพาะเจาะจงและง่ายต่อการใช้งาน

class MyClass
{
   public static function getTypeList()
   {
       return array(
           "type_a"=>"Type A",
           "type_b"=>"Type B",
           //... etc.
       );
   }
}

ทุกที่ที่คุณต้องการรายการเพียงแค่เรียกวิธีการทะเยอทะยาน ตัวอย่างเช่น:

if (array_key_exists($type, MyClass::getTypeList()) {
     // do something important...
}

11
แม้ว่านี่จะเป็นโซลูชันที่สง่างาม แต่ฉันก็ไม่ได้พูดว่ามันเหมาะอย่างยิ่งสำหรับเหตุผลด้านประสิทธิภาพส่วนใหญ่เป็นเพราะจำนวนครั้งที่อาเรย์สามารถเริ่มต้นได้ - นั่นคือการจัดสรรฮีปจำนวนมาก เนื่องจาก php เขียนขึ้นใน C ฉันคิดว่าการแปลจะแก้ไขให้ฟังก์ชันส่งคืนตัวชี้ไปยัง Array ต่อการโทรหนึ่งครั้ง ... แค่สองเซ็นต์ของฉัน
zeboidlund

นอกจากนี้การเรียกใช้ฟังก์ชันมีราคาแพงใน PHP ดังนั้นจึงเป็นการดีที่สุดที่จะหลีกเลี่ยงหากไม่จำเป็น
มาร์คโรส

14
"ดีที่สุดเพื่อหลีกเลี่ยงพวกเขาเมื่อไม่จำเป็น" - ไม่ได้จริงๆ หลีกเลี่ยงพวกเขาหากพวกเขา (อาจ) กลายเป็นคอขวด มิฉะนั้นจะเป็นการเพิ่มประสิทธิภาพก่อนวัยอันควร
Psycho brm

2
@blissfreak - หนึ่งสามารถหลีกเลี่ยงการ reallcating ถ้าเราสร้างคุณสมบัติคงที่ในชั้นเรียนและตรวจสอบใน getTypeList () ถ้ามันได้รับการเริ่มต้นแล้วและคืนนั้น หากยังไม่เริ่มต้นใช้งานให้เตรียมใช้งานและส่งคืนค่านั้น
grantwparks

12
ฉันไม่พยายามหลีกเลี่ยงการเรียกใช้ฟังก์ชันอย่างจริงจัง ฟังก์ชั่นเป็นพื้นฐานของการเขียนโปรแกรมที่มีโครงสร้าง
grantwparks

11

ฉันใช้คำตอบของ Tjeerd Visser และ porneL

class Something
{
    private static $foo;

    private static getFoo()
    {
        if ($foo === null)
            $foo = [[ complicated initializer ]]
        return $foo;
    }

    public static bar()
    {
        [[ do something with self::getFoo() ]]
    }
}

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


10

มันซับซ้อนเกินไปที่จะกำหนดในคำจำกัดความ คุณสามารถตั้งค่านิยามเป็นโมฆะและจากนั้นในตัวสร้างตรวจสอบและหากยังไม่ได้เปลี่ยน - ตั้งค่า:

private static $dates = null;
public function __construct()
{
    if (is_null(self::$dates)) {  // OR if (!is_array(self::$date))
         self::$dates = array( /* .... */);
    }
}

10
แต่ตัวสร้างจะมีส่วนช่วยในคลาสนามธรรมที่ไม่เคยมีอินสแตนซ์หรือไม่
Svish

คลาสนามธรรมไม่สามารถใช้ประโยชน์ได้เว้นแต่จะเสร็จสิ้นและสร้างอินสแตนซ์ การตั้งค่าด้านบนไม่จำเป็นต้องทำเฉพาะในตัวสร้างตราบใดที่มันถูกเรียกใช้ก่อนที่ตัวแปรจะถูกนำมาใช้
Alister Bulman

มันไม่ใช่ความคิดที่ดีที่จะสันนิษฐานว่าคอนสตรัคถูกเรียกใช้ก่อนที่จะต้องมีตัวแปรสแตติก บ่อยครั้งที่หนึ่งต้องการค่าคงที่ก่อนที่จะสร้างอินสแตนซ์
ToolmakerSteve

4

คุณไม่สามารถเรียกใช้ฟังก์ชันในรหัสนี้ได้ หากคุณสร้างเมธอด init () ที่เรียกใช้ก่อนที่โค้ดอื่นจะทำคุณจะสามารถเติมตัวแปรได้


init () วิธีการพิมพ์? ยกตัวอย่างได้ไหม เป็นเช่นเดียวกับตัวสร้างแบบคงที่ใน C #?
Svish

@Svish: ไม่ควรเรียกว่าใต้คำจำกัดความของคลาสว่าเป็นวิธีการคงที่ปกติ
Vladislav Rastrusny

4

ใน PHP 7.0.1 ฉันสามารถกำหนดสิ่งนี้ได้:

public static $kIdsByActions = array(
  MyClass1::kAction => 0,
  MyClass2::kAction => 1
);

แล้วใช้มันเช่นนี้

MyClass::$kIdsByActions[$this->mAction];

FWIW: สิ่งที่คุณแสดงไม่จำเป็นต้องใช้ PHP 7; มันทำงานได้ดีในเวลาที่ถามคำถาม: "ถ้าฉันเปลี่ยนสิ่ง mktime ด้วยสตริงปกติมันใช้งานได้" สิ่งที่หัวข้อนี้กำลังมองหาเป็นเทคนิคในการเริ่มต้นคงที่เมื่อเริ่มต้นจะต้องมีการเรียกหนึ่งหรือมากกว่าฟังก์ชั่น
ToolmakerSteve

3

วิธีที่ดีที่สุดคือการสร้างการเข้าถึงเช่นนี้:

/**
* @var object $db : map to database connection.
*/
public static $db= null; 

/**
* db Function for initializing variable.   
* @return object
*/
public static function db(){
 if( !isset(static::$db) ){
  static::$db= new \Helpers\MySQL( array(
    "hostname"=> "localhost",
    "username"=> "root",
    "password"=> "password",
    "database"=> "db_name"
    )
  );
 }
 return static::$db;
}

จากนั้นคุณสามารถทำคงที่ :: db (); หรือตนเอง :: db (); จากทุกที่


-1

นี่คือตัวชี้ที่เป็นประโยชน์หวังว่าในตัวอย่างรหัส โปรดทราบว่าฟังก์ชั่นการเริ่มต้นถูกเรียกเพียงครั้งเดียว

นอกจากนี้หากคุณสลับสายStaticClass::initializeStStateArr()และ$st = new StaticClass()คุณจะได้ผลลัพธ์เดียวกัน

$ cat static.php
<?php

class StaticClass {

  public static  $stStateArr = NULL;

  public function __construct() {
    if (!isset(self::$stStateArr)) {
      self::initializeStStateArr();
    }
  }

  public static function initializeStStateArr() {
    if (!isset(self::$stStateArr)) {
      self::$stStateArr = array('CA' => 'California', 'CO' => 'Colorado',);
      echo "In " . __FUNCTION__. "\n";
    }
  }

}

print "Starting...\n";
StaticClass::initializeStStateArr();
$st = new StaticClass();

print_r (StaticClass::$stStateArr);

ซึ่งให้ผลผลิต:

$ php static.php
Starting...
In initializeStStateArr
Array
(
    [CA] => California
    [CO] => Colorado
)

2
แต่โปรดทราบว่าคุณสร้างอินสแตนซ์ของคลาส (วัตถุ) เนื่องจากตัวสร้างเป็นฟังก์ชัน NONSTATIC สาธารณะ คำถามคือ: PHP รองรับตัวสร้างสแตติกเท่านั้น (ไม่มีการสร้างอินสแตนซ์ตัวอย่างเช่นใน Javastatic { /* some code accessing static members*/ }
Mitja Gustin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.