我有一个帮助类,其中包含一些静态函数。该类中的所有函数都需要运行一个“重型”初始化函数一次(就像它是构造函数一样)。
有没有好的实践方法可以实现这一点?
我所想到的唯一方法就是调用一个init
函数,并在其已经运行一次时中断流程(使用静态的$initialized
变量)。问题是我需要在类的每个函数中都调用它。
我有一个帮助类,其中包含一些静态函数。该类中的所有函数都需要运行一个“重型”初始化函数一次(就像它是构造函数一样)。
有没有好的实践方法可以实现这一点?
我所想到的唯一方法就是调用一个init
函数,并在其已经运行一次时中断流程(使用静态的$initialized
变量)。问题是我需要在类的每个函数中都调用它。
看起来你最好使用单例而不是一堆静态方法。
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();
getInstance()
中加入-1(但我不会这样做)……这将使有效测试非常困难......请至少将其设置为受保护的,以便您有选择...... - ircmaxell// file Foo.php
class Foo
{
static function init() { /* ... */ }
}
Foo::init();
这种方式可以在类文件被包含时进行初始化。通过使用自动加载,您可以确保只在必要时(且仅一次)进行初始化。
init
作为公共方法,如果它是私有的,它将无法工作。难道没有更干净的方式,比如Java静态类初始化器吗? - Pacerierinit()
函数在第二次调用时不执行任何操作,那么它是否公开的并不重要... static function init() { if(self::$inited) return; /* ... */ }
。 - FrancescoMMopcache.preload
不兼容。如果文件在预加载脚本中预加载,则类将“存在”,但不会出现该文件中顶层代码的效果 - 并且自动加载不需要该文件,因为类已经存在,您也不需要它,因为这会导致类被重新定义! - Szczepan Hołyszewski实际上,我在需要初始化(或至少需要执行一些代码)的静态类上使用了一个公共静态方法__init__()
。然后,在我的自动加载器中,当它加载一个类时,它会检查is_callable($class, '__init__')
。如果是这样,它就会调用那个方法。快速、简单而有效...
__initStatic()
。感觉PHP需要这样的东西,了解Java。 - Alex Pif(is_callable($class_name, "__init")) { $class_name::__init(); }
如果我使用 spl_autoload_register()
,这个方法不能用吗? - Irvan HilmiA
上而不是类B
上,但是B
继承了A
,这样不还是会被多次调用吗?当加载A
时,它将被调用一次,当加载B
时再次调用(因为通过继承它存在于B
上)。因此,基本上,每当您加载一个继承自A
的类(并且该类没有自己的__init __()
方法),A
的__init __()
方法就会再次被调用。 - Leon Williams注意:这正是楼主所说的做法。(但没有展示代码)。我在这里展示细节,这样您可以将其与被接受的答案进行比较。我的观点是,我认为楼主最初的想法比他接受的答案更好。
考虑到被接受的答案受到高度赞扬,我想指出一次性初始化静态方法的“天真”答案几乎与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();
opcode.cache
,那么请使用Victor Nicollet的答案。这很简单,不需要额外的编程。没有需要理解的“高级”编程。(我建议包括FrancescoMM的评论,以确保"init"永远不会执行两次。)请查看Szczepan的解释,了解为什么Victor的技术不适用于opcode.cache
.
如果你正在使用opcode.cache
,那么据我所知,我的答案最干净了。代价就是在每个公共方法的开头添加MyClass::init();
这一行代码。注意:如果你想要公共属性,请将它们编写为get
/set
方法对,以便有地方添加那个init
函数调用。
(私有成员不需要那个init
函数调用,因为它们无法从外部访问-因此在执行到私有成员时,某些公共方法已经被调用过。
init()
来进行初始化,否则就使用该属性的值。 - Patanjalistatic $didInit
,而不是依赖于某个特定于类的属性。这使得代码更加明显(如果您在多个类中使用相同的技术,则更加一致)。额外的内存成本是可以忽略不计的,因为它是每个类的单个静态属性(如果您创建类的实例,它不会使实例变大,而是在类本身上)。 - ToolmakerSteveMyClass
稍微变得冗长,但它使每个调用MyClass
的人更加简洁,因为他们不需要写MyClass::getInstance->someMethod1();
。在维护MyClass
时,代码就在你面前,所以如果必须要有一些冗长,那么应该是在这里。 - Adam Chalcraft有一种方法可以调用init()
方法一次并禁止它的使用,您可以将该函数转换为私有初始化程序,并在类声明后调用它,如下所示:
class Example {
private static function init() {
// do whatever needed for class initialization
}
}
(static function () {
static::init();
})->bindTo(null, Example::class)();
private init
的?你能解释一下你在这里做什么的细节吗? - ToolmakerSteveExample::class
,因此可以调用私有方法。我发现一个错误,因为init()
方法应该是static
- 已经修复了示例。 - brzuchalinit()
方法,也就是说,我们可以将所有的初始化代码直接放入这个匿名函数中,它本身就可以充当静态构造函数。 - Karolis我发布这篇文章作为答案,因为从PHP 7.4开始,这非常重要。
PHP 7.4的opcache.preload
机制可以预加载类的操作码。如果使用它来预加载包含类定义和某些副作用的文件,那么在该FPM服务器及其工作进程执行的所有后续脚本中都会“存在”于该文件中定义的类,但是副作用将不会生效,并且自动加载程序将不需要包含它们的文件,因为该类已经“存在”。这完全破坏了任何依赖于在包含类定义的文件中执行顶层代码的静态初始化技术。
分配静态公共属性的一些测试:
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
然后,如果我们像这样修改test类:
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,我一直在寻找解决这个问题的办法。
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');
?>
opcache.preload
不兼容;请参考我的答案。 - Szczepan Hołyszewski注意 - 提出此建议的RFC仍处于草案状态。
class Singleton
{
private static function __static()
{
//...
}
//...
}
建议用于 PHP 7.x(请参见https://wiki.php.net/rfc/static_class_constructor)