簡體   English   中英

Static class 初始化器在 PHP

[英]Static class initializer in PHP

我有一個幫手 class 和一些 static 函數。 class 中的所有函數都需要“重”初始化 function 才能運行一次(就好像它是構造函數一樣)。

有實現這一目標的良好做法嗎?

我唯一想到的就是調用一個init function,如果它已經運行過一次就中斷它的流程(使用 static $initialized var)。 問題是我需要在類的每一個函數上調用它。

聽起來像你最好用一個單例而不是一堆靜態方法來服務

class Singleton
{
  /**
   * 
   * @var Singleton
   */
  private static $instance;

  private function __construct()
  {
    // Your "heavy" initialization stuff here
  }

  public static function getInstance()
  {
    if ( is_null( self::$instance ) )
    {
      self::$instance = new self();
    }
    return self::$instance;
  }

  public function someMethod1()
  {
    // whatever
  }

  public function someMethod2()
  {
    // whatever
  }
}

然后,在使用中

// As opposed to this
Singleton::someMethod1();

// You'd do this
Singleton::getInstance()->someMethod1();
// file Foo.php
class Foo
{
  static function init() { /* ... */ }
}

Foo::init();

這樣,當包含類文件時就會進行初始化。 您可以通過使用自動加載來確保這僅在必要時(並且僅一次)發生。

實際上,我在需要初始化(或至少需要執行一些代碼)的靜態類上使用了公共靜態方法__init__() )。 然后,在我的自動加載器中,當它加載一個類時,它會檢查is_callable($class, '__init__') 如果是,則調用該方法。 快速、簡單、有效...

注意:這正是 OP 所說的。 (但沒有顯示代碼。)我在這里顯示了詳細信息,以便您可以將其與接受的答案進行比較。 我的觀點是 OP 的本能是,恕我直言,比他接受的答案要好。


鑒於已接受的答案的投票率如此之高,我想指出靜態方法一次性初始化的“天真”答案,幾乎沒有比單例的實現更多的代碼 - 並且具有本質優勢

final class MyClass  {
    public static function someMethod1() {
        MyClass::init();
        // whatever
    }

    public static function someMethod2() {
        MyClass::init();
        // whatever
    }


    private static $didInit = false;

    private static function init() {
        if (!self::$didInit) {
            self::$didInit = true;
            // one-time init code.
        }
    }

    // private, so can't create an instance.
    private function __construct() {
        // Nothing to do - there are no instances.
    }
}

這種方法的優點是您可以使用簡單的靜態函數語法進行調用:

MyClass::someMethod1();

將其與接受的答案所需的調用進行對比:

MyClass::getInstance->someMethod1();

作為一般原則,最好在對類進行編碼時支付一次編碼費用,以使調用者更簡單。


如果您沒有使用 PHP 7.4 的opcode.cache ,請使用Victor Nicollet 的回答 簡單的。 不需要額外的編碼。 沒有要理解的“高級”編碼。 (我建議包括 FrancescoMM 的評論,以確保“init”永遠不會執行兩次。)請參閱Szczepan對為什么 Victor 的技術不適用於opcode.cache

如果您使用的opcode.cache ,那么據我所知我的回答是那樣干凈,你可以得到。 成本只是添加行MyClass::init(); 在每個公共方法的開始。 注意:如果您想要公共屬性,請將它們編碼為get / set方法對,以便您有地方添加該init調用。

私有成員不需要那個init調用,因為它們不能從外部訪問——所以在執行到達私有成員時,已經調用了一些公共方法。)

有一種方法可以調用init()方法一次並禁止使用它,您可以將函數轉換為私有初始化程序並在類聲明后調用它,如下所示:

class Example {
    private static function init() {
        // do whatever needed for class initialization
    }
}
(static function () {
    static::init();
})->bindTo(null, Example::class)();

我將此作為答案發布,因為這在 PHP 7.4 中非常重要。

PHP 7.4 的opcache.preload機制使得為類預加載操作碼成為可能。 如果您使用它來預加載包含類定義一些副作用的文件,則該文件中定義的類將“存在”用於此 FPM 服務器及其工作程序執行的所有后續腳本,但副作用不會生效,並且自動加載器將不需要包含它們的文件,因為該類已經“存在” 這完全擊敗了任何依賴於在包含類定義的文件中執行頂級代碼的靜態初始化技術。

如果您不喜歡public靜態初始化程序,反射可以是一種解決方法。

<?php

class LanguageUtility
{
    public static function initializeClass($class)
    {
        try
        {
            // Get a static method named 'initialize'. If not found,
            // ReflectionMethod() will throw a ReflectionException.
            $ref = new \ReflectionMethod($class, 'initialize');

            // The 'initialize' method is probably 'private'.
            // Make it accessible before calling 'invoke'.
            // Note that 'setAccessible' is not available
            // before PHP version 5.3.2.
            $ref->setAccessible(true);

            // Execute the 'initialize' method.
            $ref->invoke(null);
        }   
        catch (Exception $e)
        {
        }
    }
}

class MyClass
{
    private static function initialize()
    {
    }
}

LanguageUtility::initializeClass('MyClass');

?>

分配 static 公共屬性的一些測試:

設置.json:

{
    "HOST": "website.com",
    "NB_FOR_PAGINA": 8,
    "DEF_ARR_SIZES": {
        "min": 600,
        "max": 1200
    },
    "TOKEN_TIME": 3600,
    "WEBSITE_TITLE": "My website title"
}

現在我們要將設置 public static 屬性添加到我們的 class

class test {
  
  /**  prepare an array to store datas  */
  public static $datas = array();
  
 /**
  * test::init();
  */
  public static function init(){
    
    // get json file to init.
    $get_json_settings = 
      file_get_contents(dirname(__DIR__).'/API/settings.json');

    $SETTINGS = json_decode($get_json_settings, true);
                
    foreach( $SETTINGS as $key => $value ){
         
       // set public static properties
       self::$datas[$key] = $value;         
    }

  }
 /**
  * 
  */


 /**
  * test::get_static_properties($class_name);
  *
  * @param  {type} $class_name
  * @return {log}  return all static properties of API object
  */
  public static function get_static_properties($class_name) {

    $class = new ReflectionClass($class_name);

    echo '<b>infos Class : '.$class->name.'</b><br>';

    $staticMembers = $class->getStaticProperties();

    foreach( $staticMembers as $key => $value ){

        echo '<pre>';
        echo $key. ' -> ';

        if( is_array($value) ){
            var_export($value);
        }
        else if( is_bool($value) ){

            var_export($value);
        }
        else{

            echo $value;
        }

        echo '</pre>';

    }
    // end foreach

  }
 /**
  * END test::get_static_properties();
  */

}
// end class test

好的,現在我們測試這段代碼:

// consider we have the class test in API folder
spl_autoload_register(function ($class){
    
    // call path to API folder after
    $path_API = dirname(__DIR__).'/API/' . $class . '.php';
    
    if( file_exists($path_API) ) require $path_API;
});
// end SPL auto registrer

// init class test with dynamics static properties 
test::init();
test::get_static_properties('test');
var_dump(test::$HOST);
var_dump(test::$datas['HOST']);

這個回報:

infos Class : test

datas -> array (
  'HOST' => 'website.com',
  'NB_FOR_PAGINA' => 8,
  'DEF_ARR_SIZES' => 
  array (
    'min' => 600,
    'max' => 1200,
  ),
  'TOKEN_TIME' => 3600,
  'WEBSITE_TITLE' => 'My website title'
)

// var_dump(test::$HOST);
Uncaught Error: Access to undeclared static property: 
test::$HOST
// var_dump(test::$datas['HOST']);
website.com

那么如果我們像這樣修改 class 測試:

    class test {
      
      /**  Determine empty public static properties  */
      public static $HOST;
      public static $NB_FOR_PAGINA;
      public static $DEF_ARR_SIZES;
      public static $TOKEN_TIME;
      public static $WEBSITE_TITLE;
      
     /**
      * test::init();
      */
      public static function init(){
        
        // get json file to init.
        $get_json_settings = 
          file_get_contents(dirname(__DIR__).'/API/settings.json');
    
        $SETTINGS = json_decode($get_json_settings, true);
                    
        foreach( $SETTINGS as $key => $value ){
             
           // set public static properties 
           self::${$key} = $value;                  
        }
    
      }
     /**
      * 
      */
...
}
// end class test 

// init class test with dynamics static properties 
test::init();
test::get_static_properties('test');
var_dump(test::$HOST);

這個回報:

infos Class : test
    
  HOST -> website.com
  NB_FOR_PAGINA -> 8
  DEF_ARR_SIZES -> array (
  'min' => 600,
  'max' => 1200,
)
TOKEN_TIME -> 3600
WEBSITE_TITLE -> My website title

// var_dump(test::$HOST);
website.com

我實際上需要用公共 static 屬性初始化一個 object,我將在許多其他類中重用這些屬性,我認為這是應該的,我不想在我需要的每個方法中執行 new api(),例如檢查站點的主機或指明它。 我也想讓事情變得更加動態,這樣我就可以在我的 API 中添加盡可能多的設置,而不必在我的初始化 class 中聲明它們。我見過的所有其他方法在 php > 7.4 下不再有效我保留尋找解決此問題的方法。

注意 - 提出此建議的 RFC 仍處於草案狀態。


class Singleton
{
    private static function __static()
    {
        //...
    }
    //...
}

建議用於 PHP 7.x(參見https://wiki.php.net/rfc/static_class_constructor

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM