PHP 中的静态类初始值设定项

发布于 2024-09-10 16:34:01 字数 177 浏览 10 评论 0原文

我有一个带有一些静态函数的辅助类。类中的所有函数都需要一个“重”初始化函数来运行一次(就像它是一个构造函数一样)。

有实现这一目标的良好实践吗?

我唯一想到的就是调用一个 init 函数,如果它已经运行过一次(使用静态 $initialized var),则中断它的流程。问题是我需要在类的每个函数上调用它。

I have an helper class with some static functions. All the functions in the class require a ‘heavy’ initialization function to run once (as if it were a constructor).

Is there a good practice for achieving this?

The only thing I thought of was calling an init function, and breaking its flow if it has already run once (using a static $initialized var). The problem is that I need to call it on every one of the class’s functions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

洛阳烟雨空心柳 2024-09-17 16:34:01

听起来单例比一堆静态方法更适合你

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();

Sounds like you'd be better served by a singleton rather than a bunch of static methods

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
  }
}

And then, in usage

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

// You'd do this
Singleton::getInstance()->someMethod1();
祁梦 2024-09-17 16:34:01
// file Foo.php
class Foo
{
  static function init() { /* ... */ }
}

Foo::init();

这样,初始化就会在包含类文件时发生。您可以通过使用自动加载来确保这种情况仅在必要时发生(并且仅发生一次)。

// file Foo.php
class Foo
{
  static function init() { /* ... */ }
}

Foo::init();

This way, the initialization happens when the class file is included. You can make sure this only happens when necessary (and only once) by using autoloading.

野稚 2024-09-17 16:34:01

实际上,我在需要初始化(或至少需要执行一些代码)的静态类上使用公共静态方法 __init__() 。然后,在我的自动加载器中,当它加载一个类时,它会检查 is_callable($class, '__init__') 。如果是,则调用该方法。快速、简单、有效...

Actually, I use a public static method __init__() on my static classes that require initialization (or at least need to execute some code). Then, in my autoloader, when it loads a class it checks is_callable($class, '__init__'). If it is, it calls that method. Quick, simple and effective...

野侃 2024-09-17 16:34:01

注意:这正是OP所说的。(但没有显示代码。)我在此处显示了详细信息,以便您可以将其与已接受的答案进行比较。我的观点是,恕我直言,OP 最初的直觉比他接受的答案更好。


考虑到所接受的答案得到了高度评​​价,我想指出静态方法一次性初始化的“天真的”答案,几乎不比 Singleton 的实现更多代码——并且有一个重要的优势< /强>。

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,则使用 < a href="https://stackoverflow.com/a/3312881/199364">维克多·尼科莱的回答。简单的。无需额外编码。无需理解“高级”编码。 (我建议包括 FrancescoMM 的评论,以确保“init”永远不会执行两次。)请参阅 Szczepan 的解释,了解 Victor 技术的原因不适用于 opcode.cache

如果您正在使用opcode.cache,那么据我所知,我的答案是您能得到的最干净的答案。成本只是在每个公共方法的开头添加行MyClass::init();。注意:如果您想要公共属性,请将它们编码为get / set方法对,以便您可以添加<代码>init调用。

私有成员不需要 init 调用,因为它们无法从外部访问 - 因此某些公共方法已经被调用,由执行时间到达私有成员。)

NOTE: This is exactly what OP said they did. (But didn't show code for.) I show the details here, so that you can compare it to the accepted answer. My point is that OP's original instinct was, IMHO, better than the answer he accepted.


Given how highly upvoted the accepted answer is, I'd like to point out the "naive" answer to one-time initialization of static methods, is hardly more code than that implementation of Singleton -- and has an essential advantage.

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.
    }
}

The advantage of this approach, is that you get to call with the straightforward static function syntax:

MyClass::someMethod1();

Contrast it to the calls required by the accepted answer:

MyClass::getInstance->someMethod1();

As a general principle, it is best to pay the coding price once, when you code a class, to keep callers simpler.


If you are NOT using PHP 7.4's opcode.cache, then use Victor Nicollet's answer. Simple. No extra coding required. No "advanced" coding to understand. (I recommend including FrancescoMM's comment, to make sure "init" will never execute twice.) See Szczepan's explanation of why Victor's technique won't work with opcode.cache.

If you ARE using opcode.cache, then AFAIK my answer is as clean as you can get. The cost is simply adding the line MyClass::init(); at start of every public method. NOTE: If you want public properties, code them as a get / set pair of methods, so that you have a place to add that init call.

(Private members do NOT need that init call, as they are not reachable from the outside - so some public method has already been called, by the time execution reaches the private member.)

梦旅人picnic 2024-09-17 16:34:01

有一种方法可以调用一次 init() 方法并禁止其使用,您可以将该函数转换为私有初始值设定项并在类声明后调用它,如下所示:

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

There is a way to call the init() method once and forbid it's usage, you can turn the function into private initializer and ivoke it after class declaration like this:

class Example {
    private static function init() {
        // do whatever needed for class initialization
    }
}
(static function () {
    static::init();
})->bindTo(null, Example::class)();
海风掠过北极光 2024-09-17 16:34:01

我将此作为答案发布,因为从 PHP 7.4 开始这非常重要。

PHP 7.4 的 opcache.preload 机制使得预加载类的操作码成为可能。如果您使用它来预加载包含类定义一些副作用的文件,则该文件中定义的类将“存在”于此 FPM 服务器及其工作人员执行的所有后续脚本,但是副作用不会生效,并且自动加载器将不需要包含它们的文件,因为该类已经“存在”。这完全击败了任何依赖于在包含类定义的文件中执行顶级代码的静态初始化技术。

I am posting this as an answer because this is very important as of PHP 7.4.

The opcache.preload mechanism of PHP 7.4 makes it possible to preload opcodes for classes. If you use it to preload a file that contains a class definition and some side effects, then classes defined in that file will "exist" for all subsequent scripts executed by this FPM server and its workers, but the side effects will not be in effect, and the autoloader will not require the file containing them because the class already "exists". This completely defeats any and all static initialization techniques that rely on executing top-level code in the file that contains the class definition.

公布 2024-09-17 16:34:01

这是 Victor Nicollet 和 ToolmakerSteve 两个答案的变体,旨在解决其中的一些问题,并消除在接受的答案中描述的单例模式中调用方法的稍微复杂的要求。也就是说,这种方法是:

  • 与 opcode.cache 兼容,并且不需要类本身之外的任何初始化函数调用
  • 更容易维护,因为它消除了添加 init() 的成本在每个方法内部。
  • 灵活,因为如果您有不需要初始化的方法,您只需将它们设置为公共
class Foo
{
    private static $is_init = false;
    private static function _init() {
        if (!self::$is_init) {
            self::$is_init = true;
            // do init stuff here
        }
    }
    public static function __callStatic($method, $args) {
        self::_init();
        return self::$method(...$args);
    }
    private static function method_a() {
        // this method is private so gets executed by __callStatic()
        // which handles the init process
    }
    public static function method_b() {
        // this function does not call init function because it is public
    }
}

只需像平常一样调用任何私有方法,它们就会运行_init() 如果需要:

Foo::method_a();

This is a variation of both answers by Victor Nicollet and ToolmakerSteve which seeks to solve some of the problems therein as well as eliminate the slightly more complex requirement of calling methods in the singleton pattern described in the accepted answer. Namely this approach is:

  • compatible with opcode.cache and does not require any initializing function call outside of the class itself
  • easier to maintain because it eliminates the cost of adding init() inside every method.
  • flexible because if you have methods that don't require initialization, you can simply make them public
class Foo
{
    private static $is_init = false;
    private static function _init() {
        if (!self::$is_init) {
            self::$is_init = true;
            // do init stuff here
        }
    }
    public static function __callStatic($method, $args) {
        self::_init();
        return self::$method(...$args);
    }
    private static function method_a() {
        // this method is private so gets executed by __callStatic()
        // which handles the init process
    }
    public static function method_b() {
        // this function does not call init function because it is public
    }
}

Simply call any of the private methods as you normally would and they will run _init() if needed:

Foo::method_a();
把梦留给海 2024-09-17 16:34:01

如果您不喜欢 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');

?>

If you don't like public static initializer, reflection can be a workaround.

<?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');

?>
终止放荡 2024-09-17 16:34:01

分配静态公共属性的一些测试:

settings.json:

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

现在我们要向我们的类添加设置公共静态属性,

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 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

我实际上需要使用公共静态属性初始化一个对象,我将在许多其他类中重用该对象,我认为这是应该的,我不想在我需要的每个方法中执行 new api() ,例如检查主机网站或指出。另外,我想让事情变得更加动态,这样我就可以向 API 添加尽可能多的设置,而不必在初始化类中声明它们。
我见过的所有其他方法都不再在 php > 下工作7.4
我一直在寻找这个问题的解决方案。

Some tests of assigning static public properties :

settings.json :

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

now we want to add settings public static properties to our 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

ok now we test this code :

// 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']);

this return :

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

Then if we modify the class test like this :

    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);

this return :

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

I actually need to initialize an object with public static properties that I will reuse in many other classes, which I think is supposed to, I don't want to do new api() in every method where I would need, for example to check the host of the site or indicate it. Also I would like to make things more dynamic so that I can add as many settings as I want to my API, without having to declare them in my initialization class.
All other methods I've seen no longer work under php > 7.4
I keep looking for a solution for this problem.

握住你手 2024-09-17 16:34:01

注意 - 提出此建议的 RFC 仍处于草案状态。


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

建议 PHP 7.x(请参阅 https://wiki.php.net/rfc/static_class_constructor

Note - the RFC proposing this is still in the draft state.


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

proposed for PHP 7.x (see https://wiki.php.net/rfc/static_class_constructor )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文