I am trying to figure out how I want to handle settings in my PHP app. I have pretty much decide that I would like to use a Confg class file so it will be autoloaded and flexible in the future. Below is some stuff I was playing with.
I know you cannot set a variable to popluate a Constant so I then try to use a public static property.
Why can I not set public static $ip = $_SERVER['REMOTE_ADDR']; ??
<?php
// config.class.php
class Config
{
const URL = 'http://www.foo.com';
const DB_User = 'dbname';
public static $test = 'test string';
public static $ip = $_SERVER['REMOTE_ADDR'];
}
///////////////////////////////////////////////////////
//index.php
// works
echo Config::URL;
// works
echo Config::$test;
// DOES NOT WORK
echo Config::$ip;
?>
members must be initialized with a constant expression (like a string constant, numeric literal, etc). php will give a parse error if you try to initialize a member with a dynamic expression (like the value of a variable or a function call)...
this is unlike some other langs like python, or javascript which consider class definitions to be on par with executable expressions... so your idea here is very functional, but php doesn't support it now at least...
but there are some alternatives to deal with this:
add the initialization after the class definition:
class C {...}
C::$var = $_SERVER['REMOTE_ADDR'];
or have an init function:
function init()
{
if (self::$init === false) {
self::$var = $_SERVER['REMOTE_ADDR'];
self::$init = true;
}
}
C::init();
or with newer php you can use the __autoload() hook to do some static initializations...
Although this does not answer your question (jspcal answers it correctly), a quick solution that might fit your needs would to use the Singleton design pattern. Here is an alternative:
<?php
// config.class.php
class Config
{
/**
* Instance container
* #var Config
*/
private static $instance = null;
/**
* Constants container
* #var array
*/
private $constants = array(
'url' => 'http://www.foo.com/',
'db_user' => 'dbname'
);
/**
* Options container
* #var array
*/
private $options = array();
/**
* Don't allow outside initialization
*/
private function __construct()
{
// Set options (could load from external source)
$this->options = array(
'test' => 'test string',
'ip' => $_SERVER['REMOTE_ADDR']
);
}
/**
* Disable cloning
*/
private function __clone() { }
/**
* Get new instance of class
*/
public function getInstance()
{
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Retrieve value with constants being a higher priority
* #param $key Key to get
*/
public function __get( $key )
{
if ( isset( $this->constants[$key] ) ) {
return $this->constants[$key];
} elseif ( isset( $this->options[$key] ) ) {
return $this->options[$key];
}
}
/**
* Set a new or update a key / value pair
* #param $key Key to set
* #param $value Value to set
*/
public function __set( $key, $value )
{
$this->options[$key] = $value;
}
}
///////////////////////////////////////////////////////
//index.php
// works
$config = Config::getInstance();
echo $config->url;
echo $config->test;
echo $config->ip;
Updated: Not sure if you want the constants / options with that kind of priority. It's just an example.
try to use define() to do that (give a constant!):
// config.class.php
define(REMOTE_ADDR, $_SERVER['REMOTE_ADDR']);
class Config
{
const URL = 'http://www.foo.com';
const DB_User = 'dbname';
public static $test = 'test string';
public static $ip = REMOTE_ADDR;
}
Not a direct answer to your question, but why don't you use a less hardcoded approach, e.g. a generic Config class you can reuse in your apps
// MyConfig.php
class MyConfig {
protected $_data;
public function __construct($path)
{
$config = include $path;
$this->_data = $config;
}
public function __get($val)
{
if(array_key_exists($val, $this->_data)) {
return $this->_data['val'];
} else {
trigger_error("Key $val does not exist", E_USER_NOTICE);
}
}
}
that you can fill from an array for a specific app
// app-config.php
return array(
'ip' => $_SERVER['REMOTE_ADDR'],
'url' => 'http://www.foo.com';
'db' => array(
'host' => 'foo.com',
'port' => 3306
),
'caching' => array(
'enabled' => false
)
);
and then use in your bootstrap like
$config = new MyConfig('/path/to/app-config.php');
This is not answering your question, but in my opinion, a better way to deal with configurations is actually to use a real configuration file, like an INI or XML file.
You could use e.g. the Zend Config class to read and write such files (and this class can even deal with a plain PHP array as configuration.
In the end this will make your code easier to maintain.
After reading other answers and comments you might also be interested in the Zend Registry class.
In general I would advice to use a framework or readymade components for such stuff. You don't need to reinvent the wheel and you can profit from the other's experience.
It won't work because PHP just doesn't allow it.
Generally I wouldn't suggest putting your app's config into a class (at least not in PHP), there are no real advantages in my opinion - better put it into a global array or so :)
PHP simply doesn't allow that. If you think about it, it doesn't make sense to have the REMOTE_ADDR as part of your configuration data anyway. You can consider it to be part of the request data, and not the application configuration.
As for your configuration, I would suggest using an .ini file for your configuration because PHP has a built-in INI parser (see the parse_ini_file function). This would be consistent with PHP's choice of INI file format for its own configuration (i.e., php.ini).
Check out this question, for example: What's Is the Best File Format for Configuration Files
The simple reason why you cannot assign $_SERVER['REMOTE_ADDR'] to the class attribute is this.
In PHP, Class attribute value can only be a data type and not a variable, and since $_SERVER is a global array variable, it can't be assigned to an attribute.
Related
In CodeIgniter, how can I pass parameters from the context where the hook is called?
Example:
File_Where_Hook_is_Called.php:
$this->hooks = load_class('Hooks', 'core');
$filename = 'example.zip';
$this->hooks->call_hook('site_export_before_delete');
config/hooks.php:
$hook['site_export_before_delete'][] = array(
'class' => 'Foo',
'function' => 'export_site',
'filename' => 'Foo.php',
'filepath' => 'modules/foo/controllers',
'params' => [''] # Should be $filename from File_Where_Hook_is_Called.php
);
application/modules/foo/controllers/Foo.php:
public function export_site($filename) {
echo $filename); # Should print $filename from File_Where_Hook_is_Called.php
}
I know it may be a bit late but I stumbled upon your question as I had the same need and I solved it in a correct manner.
According to the CodeIgniter documentation you can override the default CI_Hooks class behavior by implementing your own MY_Hooks class, usually at application/core/MY_Hooks.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class MY_Hooks extends CI_Hooks{
protected function _call_single_hook($data, $params) {
if($params) {
if(!isset($data['params'])) {
$data['params'] = [];
}
$data['params'] = array_merge($data['params'], $params);
}
$this->_run_hook($data);
}
/**
* Call Hook
*
* Calls a particular hook. Called by CodeIgniter.php.
*
* #uses CI_Hooks::_run_hook()
*
* #param string $which Hook name
* #param array $params Extra (dynamic) parameters array
* #return bool TRUE on success or FALSE on failure
*/
public function call_hook($which = '', $params=null)
{
if ( ! $this->enabled OR ! isset($this->hooks[$which]))
{
return FALSE;
}
if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
{
foreach ($this->hooks[$which] as $val)
{
$this->_call_single_hook($val, $params);
}
}
else
{
$this->_call_single_hook($this->hooks[$which], $params);
}
return TRUE;
}
}
In my case I extended the CI_Hooks class just overwriting the call_hook method, and the approach was appending extra parameters to the allowed "params" key of the hooks array.
After that you can invoke the hook from your controller.
Based upon your example, the code would be:
$this->hooks->call_hook('site_export_before_delete', ['filename' => $filename]);
From my point of view, this is the good solution for the problem, and it's working for me.
It sucks, but use global scoped variable.
Since the function call_hook under CI_Hook class, does not take another parameter:
public function call_hook($which = '') {
#...
}
And since I don't want to modify core files from CodeIgniter directly, I ended up using a global variable scope:
File_Where_Hook_is_Called.php:
# A variable with global scope should have an unique name,
# to avoid conflict with any other one in your entire application.
# Choose it carefully.
global $foo_hook_file_name;
$foo_hook_file_name = $file_name;
application/modules/foo/controllers/Foo.php:
public function export_site() {
global $foo_hook_file_name;
echo $foo_hook_file_name;
}
Just make sure to prefix the global var with a unique name, such as the name of the hook, to avoid conflicts with other variables.
I am currently refactoring code from a page parser function to OOP.
I am having difficulties including and running code from a file into main function scope:
Object:
class phpFragment {
private $sData;
function render() {
return include $oElement->sData;
}
}
Object container class:
class pageData {
protected $aPhpFragments;
protected $aCssFragments;
public function outputData($sTag) {
switch($sTag) {
case 'php':
foreach($this->aPhpFragments as $oPhpFragment) {
return $oPhpFragment->render();
}
break;
case 'css':
foreach($this->aCssFragments as $oCssFragment) {
echo $oCssFragment->render();
}
break;
}
}
}
Main function:
function parsePage($sLanguageCode) {
$oTranslator = new translator($sLanguageCode);
$aTranslations = $oTranslator->translations('page');
$oBuilderClass = new builder($aTranslations);
//... queries to get data and set pagedata and get the template file
$oPageData = $oPage->getData();
$aTemplateTags = $oTemplate->getTags();
foreach($aTemplateTags as $sTag) {
$oPageData->outputData($sTag);
}
//....
}
Code of include (example):
<?php
$oBuilderClass->build_element(.... parameters here);
?>
I want to initiate the builder-class only once, because it contains quite some data and I don't want to recreate that on every include.
How can I return the code of the include into the parsePage function where the builderClass can be used?
You can create a Context class that will be a container of your scope variables and helps you include (execute) code inside a context. It will be a singleton class (only one instance will be created).
Here is how to use it: The method current() returns the current instance then you can export variables to the context by using the export() method, it takes a key/value array. The method execute() takes a file name as a parameter and includes it with the exported variables available, you can add temporary variables as a second parameter:
//Somewhere before execute();
oContext::current()->export([
'variable1' => 'value1',
'instance' => $instance
]);
//Then anywhere in your file:
oContext::current()->execute("toBeIncluded.php", [
'tmp_variable' => 'tmp_value'
]);
//toBeIncluded.php
echo $variable1;
echo $instance->method1();
echo $tmp_variable;
In your case:
Main function:
function parsePage($sLanguageCode) {
$oTranslator = new translator($sLanguageCode);
$aTranslations = $oTranslator->translations('page');
$oBuilderClass = new builder($aTranslations);
//export variables to your context
//Don't be aware of memroy usage objects are passed by reference
oContext::current()->export(compact('oBuilderClass'));
//... queries to get data and set pagedata and get the template file
$oPageData = $oPage->getData();
$aTemplateTags = $oTemplate->getTags();
foreach($aTemplateTags as $sTag) {
$oPageData->outputData($sTag);
}
//....
}
Object:
class phpFragment {
private $sData;
function render() {
oContext::current()->execute($oElement->sData);
}
}
You find bellow the class declaration:
oContext.class.php
/**
* Class oContext
*/
class oContext {
/**
* The singleton instance
* #var oContext
*/
private static $instance = null;
/**
* the exported variables
* #var array
*/
private $variables = [];
/**
* Return the singleton or create one if does not exist
*
* #return oContext
*/
public static function current() {
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Export an array of key/value variables
*
* #param $variables
* #return $this
*/
public function export($variables) {
foreach ($variables as $key => $value) {
$this->variables[$key] = $value;
}
return $this;
}
/**
* Include and execute a file in this context
*
* #param $file
* #param array $variables temporary exports will not be added to the context (not available in the next call)
* #return $this
*/
public function execute($file, $variables = []) {
//Populate variables
foreach (array_merge($this->variables, $variables) as $key => $value) {
${$key} = $value;
}
include $file;
return $this;
}
}
I hope this help you achieve your aim.
Good Luck.
If I correctly understand your problem then you want to execute a whole code from php file as a method called from object. If yes then you probably want to use a eval function described here.
With eval function you can read your php file as a string and evaluate it as php code instead of including it.
If your php file use a return statement then following by documentation
eval() returns NULL unless return is called in the evaluated code, in
which case the value passed to return is returned.
you can simply return that value from your method.
If your included files are as simple as you show in example then to achieve this effect you need to replace this part of your code
class phpFragment {
private $sData;
function render() {
return include $oElement->sData;
}
}
with this
class phpFragment {
private $sData;
function render() {
//read a file into variable as string
$phpCode = file_get_contents($oElement->sData);
//prepare code by adding return statement and '?>' at the begining (because you have an open tag in php files).
$phpCode = '?> ' . str_replace('$oBuilderClass->build_element', 'return $oBuilderClass->build_element', $phpCode);
//I guess that included files don't use any variables declared out of file so we need to simply escape every '$' character in file
//that they can evaluate correctly.
$phpCode = str_replace('$', '\$', $phpCode);
return eval($phpCode);
}
}
Sounds like a dependency injection problem: you want $oBuilderClass to be in scope inside the include code?
If you have access to an application dependency container, I'd register the object with that container. In generic terms, something like \Application::bind('Builder', $oBuilderClass), then later do Builder::build_element. However, that you are writing your own view renderer suggests you don't have access to a framework facility with a formal IoC container.
Supposing you don't have an IoC container, the most expedient way would be to do:
$GLOBALS['oBuilderClass'] = new builder(...);
then later in your include:
global $oBuilderClass;
$oBuilderClass->build_element(...);
This is not particularly elegant, however. You might consider passing the builder around, so that at the bottom of the call well you have:
function render(builder $oBuilderClass) {
return include $oElement->sData;
}
which puts $oBuilderClass in scope at the time of the include. I would prefer a formal IoC container first, then passing the object around, then finally if none of these work for you, then using the global variable.
I'm trying to write some abstract MVC classes in PHP using PHPStorm. Some of the overriding class properties I am using for generic class wrappers tend to be better suited to using static methods and properties.
The code works fine, but whenever I use a variable to represent a class name when pointing it at a static variable (one that exists in inherited methods) PHPStorm doesn't know how to handle it and shows it as an error in the IDE. I'm just wondering if there's a better way to do this sort of thing or if it's simply going to be something I have to learn to ignore in the IDE.
Example:
class AbstractModel {
protected static $_dataMapper = null;
protected static $_subs = null;
public function __construct($data=null) {
// constructor omitted with things like a generic class init, etc.
}
// $_subs is a static array who's value is a class name use for
// populating sub-objects as part of a class instance
public function setSubProperty($subKeyName,$value) {
$dataType = get_called_class();
/*************************************
* PHPStorm complains here on $_subs *
*************************************/
if(is_array($dataType::$_subs)
&& array_key_exists($subKeyName,$dataType::$_subs)) {
$setMethod = 'set' . ucfirst($subKeyName);
return $dataType->$setMethod($value);
} else {
return false; // or throw an exception
}
}
}
class SomedataModel extends AbstractModel {
public $other = null;
protected static $_subs = array(
'other' => "OtherModel"
);
public function __construct($data=null) {
parent::__construct($data=null);
foreach(array_keys(self::$_subs) as $_k) {
$setMethod = 'set'.ucfirst($_k);
$this->$setMethod();
}
}
public function setOther($data=null) {
// sanitize and set instance of 'other' as $this->other
}
}
You can easily work around that by using the static keyword instead:
...
public function setSubProperty($subKeyName,$value) {
if (is_array(static::$_subs)
&& array_key_exists($subKeyName, static::$_subs)) {
$setMethod = 'set' . ucfirst($subKeyName);
...
PHPStorm supports it very well. Support of variable string values as classnames resolution of static members and properties is not supported. You might want to open a feature request (if it does not yet exists), however I doubt it's technically feasible because it wouldn't be type-hinting but value-hinting which I think is not supported in Phpstorm.
Previously a class which I'm now rebuilding had a member variable $settings which was an array of settings, strangely enough.
class MyClass {
public $settings = array();
public function __construct() {
if( empty( $this->settings ) ) {
$this->settings = require( 'settings.php' ); // e.g. return array('setting1'=>4);
}
}
}
These settings were accessed by $object->settings['keyname'];
The means by which these keys are accessed has been moved into a method now. However, the application itself is riddled with calls to $object->settings['keyname']. I was wondering is there a way which I can catch any calls to the $settings member variable and return it using the new function.
I've looked at __get($name) but $name only contains settings rather than the array key which I need. What I'd need to pass would be the keyname to the my $object->get() method.
The reason I want to do this is so that I can trigger errors in a log file showing me where the deprecated calls to $object->settings[] are without breaking the application. Obviously setting $setting to private would give me lots of fatal errors and I could work through but there are multiple developers working on this codebase which I'd prefer not to break. If I could implement this as a temporary solution it'd help.
I realise there are repositories etc which we could use so that I could work on it separately and check it in afterwards but I'm looking for a quick, temporary solution as we're porting our codebase to Git soonish.
Totally possible:
<?php
class Logger {
public function log( $name, $backtrace ) {
/**
* $name is the name of the array index that was called on
* MyClass->settings( ). $backtrace contains an array in which
* you can find and determine which file has accessed that index,
* and on which line.
*/
$last = array_shift( $backtrace );
$message = sprintf( "Setting '%s' was called by file '%s' on line %d",
$name,
$last['file'],
$last['line']
);
echo $message;
}
}
class MyClass {
protected $settings;
public function __construct( ) {
$this->settings = new Settings( new Logger( ), array( 'foo' => 'bar' ) );
}
public function __get( $name ) {
if( $name === 'settings' ) {
return $this->settings;
}
}
}
class Settings extends ArrayObject {
protected $logger;
protected $settings;
public function __construct( Logger $logger, array $settings ) {
$this->logger = $logger;
parent::__construct( $settings );
}
public function offsetGet( $name ) {
$backtrace = debug_backtrace( );
$this->logger->log( $name, $backtrace );
return parent::offsetGet( $name );
}
}
$myClass = new MyClass( );
echo $myClass->settings['foo'] . "\n";
Sure, it's hackish, and you probably don't want to keep this in your codebase, but for logging deprecated uses, it might be extremely helpful. Just log for a set period of time, and then replace the $this->settings = new Settings( ) with $this->settings = array( ).
By the way, the output of that exact code is the following:
berry#berry-pc:~/Desktop% php foo.php
Setting 'foo' was called by file '/home/berry/Desktop/foo.php' on line 53
bar
To find where scripts SET a variable in settings:
I would suggest putting a tiny dirty hack in the constructor and the destructor of your MyClass class. In the constructor scan the $settings and check if a deprecated value exists (save it somewhere temp). In the destructor do the same check, and cross match the two results. When you have new variables during the destruction you know that the $_SERVER['PHP_SELF'] has set it.
To find where scripts access it:
Brutal, but I would recommend just converting all deprecated values to new ones, and watch things stop working. You'll spend less time tracking down what broke, and reading the "This used to work and doesn't" complaints, than you will modifying your class to use a logger
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.
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();
// 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.
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...
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.)
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)();
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.
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');
?>
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.
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 )